Skip to content

Add per-project non-guessable meeting URLs (#72)#143

Open
suiyangqiu wants to merge 2 commits into
masterfrom
feature/72-obfuscated-meeting-urls
Open

Add per-project non-guessable meeting URLs (#72)#143
suiyangqiu wants to merge 2 commits into
masterfrom
feature/72-obfuscated-meeting-urls

Conversation

@suiyangqiu

Copy link
Copy Markdown
Member

📝 Summary of Changes

Corresponding issue: #72

  • Adds a per-project setting that switches a project's meeting dashboards from guessable, human-readable URLs (/{project}/{meeting-id}/) to non-guessable ones (/{project}/{guid}/). When it is on, the public per-project index page (which listed every meeting) is suppressed too, so the only way to reach a dashboard is the exact link sent in the Teams notification.
  • Default stays human-readable and existing dashboard URLs are untouched, so nothing changes for a project until it is explicitly opted in.

🤷‍♂️ Why

Tiger's dashboard URLs embed the project and meeting date, so anyone who knows a project can guess links to sensitive sprint retros, and the auto-generated index page hands out the full list. Projects that care about privacy need a way to make those URLs undiscoverable. The toggle is per-project so security-sensitive teams can turn it on without affecting everyone else.

🧪 Test plan

  • npm test - 82 unit tests pass, including new coverage for the settings lookup (default-safe when Cosmos is absent) and the deploy-slug decision (human-readable vs fresh GUID vs stable reuse) plus index suppression.
  • Drove the real deployer functions through an inline harness: OFF -> crm/2026-04-03/; ON -> crm/<uuid>/; ON with a prior GUID -> same URL reused; project index -> suppressed (returns null).
  • Live Azure e2e (deploy a real dashboard to satigertestweb with the flag on) is pending creation of the new projects Cosmos container via infra/setup-cosmos.sh test. That container-create needs an ARM DocumentDB/Contributor role; the authoring account only holds Key Vault Administrator on cosmos-tiger-*, so a sysadmin needs to run it once per environment.
  • After the container exists: node scripts/set-project-obfuscation.js <project> true, process a meeting, confirm the Teams link is a GUID URL and /{project}/ returns no index.

🔒 Note on changing the flag

Tiger is a public repo, but flipping the setting writes to the projects Cosmos container, gated by Azure RBAC (Cosmos DB Built-in Data Contributor on cosmos-tiger-*) - not by repo access. The admin script holds no credentials (DefaultAzureCredential + COSMOS_ENDPOINT only). So the control point is Cosmos role membership, and this does not need to be a Keeper secret.

🤖 Generated with Claude Code

suiyangqiu and others added 2 commits July 10, 2026 12:49
Adds a per-project obfuscateUrls setting stored in a new Cosmos "projects"
container. When enabled, new dashboards deploy to a non-guessable
{project}/{guid}/ path (GUID reused from Cosmos on re-processing so the URL
is stable), and the public per-project index page is suppressed so it can't
re-expose the links. Default is unchanged (human-readable
{project}/{meeting-id}/), and existing dashboards keep their URLs.

The setting is read at deploy time via lib/cosmosClient.getProjectSettings,
which is default-safe (returns human-readable if Cosmos is missing or
unreachable, so a deploy never breaks on the settings store). A small admin
script (scripts/set-project-obfuscation.js) flips the flag; changing it needs
the Cosmos data-plane role via Azure RBAC, not repo access.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- getProjectSettings now fails CLOSED: it throws on any non-404 Cosmos
  error (network/auth/throttle/5xx) so an ambiguous outage aborts the
  deploy instead of silently downgrading an opted-in project to a
  guessable URL and re-publishing the public index. 404 (missing
  item/container/db = not opted in) and no-endpoint still default to
  human-readable.
- deployProjectIndex now actively deletes any existing
  {project}/index.html for obfuscated projects (idempotent, non-fatal)
  rather than just skipping the write, so a landing page published
  before opt-in stops listing historical meetings.
- resolveDeploySlug gains a dependency-injection seam; add tests for the
  Cosmos-backed stable-GUID reuse, fresh-GUID, lookup-failure, and
  fail-closed-abort paths.
- Extract projectDocId() helper (single source of truth for the settings
  doc id) and document the security model: a GUID URL is an undiscoverable
  bearer-token link, not access control, and relies on the $web container
  keeping anonymous listing disabled (noted in dashboardStorage.bicep).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@suiyangqiu
suiyangqiu marked this pull request as ready for review July 10, 2026 05:52

@yaqi-lyu yaqi-lyu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<a href="./${encodeURIComponent(meeting.meetingId)}/" class="block bg-white rounded-xl shadow-sm ssw-card p-6">

In the projectIndex.js, it still uses meetingId. Does that have some affect on this pbi?

Comment thread processor/deployer.js
Comment on lines +293 to +299
} catch (err) {
const stderr = String(err.stderr || "");
if (/BlobNotFound|does not exist/i.test(stderr)) {
log("info", "No existing project index to remove for obfuscated project", { blobName });
} else {
log("warn", "Could not remove existing project index (non-fatal)", { blobName, stderr });
}

@yaqi-lyu yaqi-lyu Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If deleting the old project index fails, we should fail the non 404 deploy instead of logging and continuing. /{project}/index.html may stay public and keep listing old meeting links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants