Skip to content

feat(auth): let administrators turn public links off for the installation - #20

Open
juancarlosm wants to merge 18 commits into
mainfrom
feat/disable-public-links
Open

juancarlosm wants to merge 18 commits into
mainfrom
feat/disable-public-links

Conversation

@juancarlosm

Copy link
Copy Markdown
Contributor

Summary

Implements AUTH-006. A human administrator turns public links off or on in Settings › Public links, unless the operator sets and locks the policy at startup with ARTIFACT_SERVER_PUBLIC_LINKS_ENABLED=true|false.

While public links are off:

  • No surface can make an artifact Public: HTTP, MCP, CLI --public, upload commit. These return 409 PUBLIC_LINKS_DISABLED. Authorization is checked first, and lowering always works.
  • Artifacts already Public keep their level, but they stop opening without sign-in. Anonymous readers get exactly the Organization answer (401 CONTENT_NOT_PUBLIC, private, no-store, never 304). Signed-in readers still open them as Organization would allow.
  • Turning public links back on restores those artifacts unchanged.

Spec: project/spec/disable-public-links-spec.md; decision 0033.

Changes

  • Storage: a per-installation publicLinksEnabled switch, on by default. SQLite schema 16 and D1 v13 add an installation_settings table; Postgres migration 0015 adds a column on artifact_installations. The value is never cached in a process.
  • Policy: PublicLinkPolicyService is the only place that resolves the effective value: the configured value if set, otherwise the stored one. Admin-only GET and PUT /api/v1/administration/public-link-policy. A locked policy refuses changes with 409 PUBLIC_LINK_POLICY_LOCKED.
  • Enforcement: raises are refused in changeAccess and publishPreparedNew, after the replay lookup. Anonymous reads are refused in authorizeVersionContent and resolvePublicArtifact. MCP artifact_open returns a sign-in link while off.
  • Configuration: the variable is read by start, start-compact, start-external-storage and the Cloudflare worker. Helm configuration.publicLinksEnabled renders false too. Compose passes it through, and .env.example documents it. An invalid value fails startup.
  • Session and MCP: the session gains installation.publicLinksEnabled. MCP artifact_capabilities drops public_link from sharing.modes while off.
  • Review UI:
    • Settings switch with in-place confirmations: the turn-off one shows the Public count and what happens to those artifacts.
    • Locked state that names the variable.
    • Share modal shows "Public — Turned off by an administrator".
  • Docs: product spec, admin spec, docs/mcp.md, skill reference, ledger acceptance text.

Test Plan

Verified locally (light checks only; the full gates run on a separate machine):

  • lint, typecheck (root, web, Cloudflare package), Helm static check
  • ledger checks: validate-conformance.rb, check-conformance-test-ids.rb
  • targeted vitest: AUTH-006-F, storage, CLI start paths, MCP, content access
  • pnpm verify:iteration (full vitest, writes local-foundation.json)
  • Playwright AUTH-006-B and the locked-state test (never run yet)
  • Cloudflare workerd suite incl. public-link-policy-runtime and d1-public-link-policy
  • pnpm verify:external-storage-runtime (migration 0015, two-process switch test)
  • scripts/verify-helm-chart.sh, pnpm smoke
  • pnpm perf:baseline / verify:external-storage-performance against main
  • Evidence attached, AUTH-006 → behavior_verified, spec + decision status updated

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p

juancarlosm and others added 18 commits October 31, 2026 07:21
Administrators turn public links off and on in Settings, unless
ARTIFACT_SERVER_PUBLIC_LINKS_ENABLED sets and locks the policy. While
off, nothing can be made Public and stored-Public artifacts need
sign-in but keep their level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
ARTIFACT_SERVER_PUBLIC_LINKS_ENABLED=true|false sets the policy on every
deployment and locks the Settings switch. Unset leaves the choice to
administrators.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
The Worker validates the value before setup, and the conformance test
proves refused PUTs under a lock write nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwpo6f39DCmJkJwz5ahK1p
AUTH-006 moves to behavior_verified: AUTH-006-B with the Playwright
run and AUTH-006-F with the local vitest run. The disable-public-links
spec and decision 0033 are marked accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant