Commit c71840

2026-03-15 02:43:01 Claude (MCP): [mcp] Rewrite VPS phases: add VS (auth spikes) phase, renumber dependency graph
Tasks/VPS_Phases.md ..
@@ 9,7 9,7 @@
Implementation sequence for robot.wtf on the Debian 12 / Proxmox VM. Phase designations prefixed with **V** to distinguish from the archived AWS phases (P0–P4).
- See [[Design/VPS_Architecture]] for the architecture these phases build toward.
+ See [[Design/VPS_Architecture]] for the architecture these phases build toward. See [[Dev/V3_V5_Risk_Research]] for the auth risk assessment informing the spike phase.
---
@@ 56,6 56,65 @@
---
+ ## VS: Auth Spikes
+
+ **Goal:** Validate both auth flows empirically before building the real thing. Kill unknowns early.
+
+ These are throwaway prototypes, not production code. The point is to prove the protocol flows work against real counterparts (bsky.social PDS, Claude.ai MCP client) so that V3 and V5 are pure implementation, not discovery.
+
+ ### VS-1: ATProto OAuth Spike
+
+ Deploy the Bluesky cookbook Flask demo (`bluesky-social/cookbook/python-oauth-web-app`) directly on the VM behind Caddy. Minimal adaptation — just enough to run it at `robot.wtf/auth/` with the robot.wtf client JWK.
+
+ **Tasks:**
+
+ - VS-1a: Clone the cookbook demo, install deps, configure Caddy to proxy `robot.wtf/auth/*` to the demo on a local port.
+ - VS-1b: Publish client metadata at `https://robot.wtf/auth/client-metadata.json` with scope `"atproto"` (identity-only) and redirect URI `https://robot.wtf/auth/oauth/callback`.
+ - VS-1c: Log in with a real Bluesky account. Walk through the full flow: handle entry → PDS redirect → approve → callback → token → DID.
+ - VS-1d: Test with a non-bsky.social PDS if one is accessible (e.g., a self-hosted PDS on the ATProto community server). This validates that the flow isn't accidentally Bluesky-specific.
+ - VS-1e: Document what worked, what didn't, any PDS quirks encountered.
+
+ **Exit criteria:**
+ - A real Bluesky login completes end-to-end on robot.wtf
+ - The DID and handle are retrieved from the token response
+ - DPoP nonce handling works without manual intervention
+ - Findings documented in a Dev summary page
+
+ **Expected time:** Half a day. The demo is ready to run.
+
+ ### VS-2: MCP OAuth AS Stub
+
+ Build a minimal, hard-coded OAuth 2.1 AS that implements the five endpoints Claude.ai needs. No real auth, no database, no ATProto — just the protocol surface with canned responses. Deploy behind Caddy and point Claude.ai at it.
+
+ **Tasks:**
+
+ - VS-2a: Implement the stub AS as a single Flask file (~150 lines):
+ - `GET /.well-known/oauth-authorization-server` → static JSON metadata
+ - `POST /auth/oauth/register` → accept any DCR request, return a `client_id` + `client_secret`
+ - `GET /auth/oauth/authorize` → auto-approve (no consent UI), redirect with auth code
+ - `POST /auth/oauth/token` → exchange code for a JWT access token (hard-coded claims)
+ - `GET /.well-known/jwks.json` → serve the RS256 public key
+ - VS-2b: Implement the protected resource metadata on a wiki subdomain:
+ - `GET https://{slug}.robot.wtf/.well-known/oauth-protected-resource` → point to robot.wtf AS
+ - VS-2c: Deploy a minimal MCP endpoint (FastMCP with one dummy tool, e.g., `echo`) behind the protected resource metadata. Wire it to return 401 with `WWW-Authenticate` header on unauthenticated requests, and accept the stub JWT on authenticated requests.
+ - VS-2d: Add the MCP URL in Claude.ai settings. Walk through the flow: Claude.ai discovers AS → registers → redirects to authorize → gets token → calls the echo tool.
+ - VS-2e: If it fails, inspect what Claude.ai actually sent (log all requests) and iterate. Document the exact request shapes, headers, and timing.
+ - VS-2f: Test the same flow with Claude Code (`claude mcp add --transport http`) to verify OAuth discovery works there too.
+
+ **Exit criteria:**
+ - Claude.ai completes the MCP OAuth flow against the stub and successfully calls a tool
+ - OR: documented exactly where/why Claude.ai's client diverges from the spec, with a plan to accommodate
+ - Claude Code OAuth discovery tested
+ - Findings documented in a Dev summary page
+
+ **Expected time:** 1–2 days, mostly debugging Claude.ai's behavior.
+
+ ### Why spike before building
+
+ The research ([[Dev/V3_V5_Risk_Research]]) assessed V3 as low risk and V5 as medium risk, but both assessments are based on documentation and code reading, not empirical testing. The spikes turn "should work" into "does work" for a cost of ~2 days, before committing to the full implementation in V3 and V5. If either spike reveals a fundamental incompatibility, we find out before building the real system, not after.
+
+ ---
+
## V1: Otterwiki on Caddy
**Goal:** Single Otterwiki instance running behind Gunicorn behind Caddy, with multi-tenant slug-based routing on wildcard subdomains. No auth yet — test users hardcoded in SQLite.
@@ 104,13 163,13 @@
**Goal:** Real users can sign in with their Bluesky handle and get a platform JWT.
- This is one of the two big chunks of new work (the other is V5). The Bluesky cookbook Flask demo (`bluesky-social/cookbook/python-oauth-web-app`, CC-0 licensed) is the starting point.
+ VS-1 proved the flow works. This phase replaces the spike with production code: proper error handling, session management, signup flow, and integration with the TenantResolver.
**Tasks:**
- - V3-1: Auth service scaffold. Flask app on port 8003. Caddy routes `robot.wtf/auth/*` to it.
- - V3-2: Publish ATProto OAuth client metadata at `https://robot.wtf/auth/client-metadata.json`. This URL becomes the `client_id` in the ATProto OAuth protocol — it must be stable.
- - V3-3: Implement the ATProto OAuth flow. Adapt the Bluesky cookbook demo: handle input → DID resolution → PDS discovery → PAR → redirect → callback → token exchange → profile fetch. Store ATProto tokens in the `oauth_sessions` table.
+ - V3-1: Auth service scaffold. Flask app on port 8003. Caddy routes `robot.wtf/auth/*` to it. (May already exist from VS-1 — evolve the spike or rewrite clean.)
+ - V3-2: Publish ATProto OAuth client metadata at `https://robot.wtf/auth/client-metadata.json`. This URL becomes the `client_id` in the ATProto OAuth protocol — it must be stable. (May already exist from VS-1.)
+ - V3-3: Implement the ATProto OAuth flow. Adapt the Bluesky cookbook demo: handle input → DID resolution → PDS discovery → PAR → redirect → callback → token exchange → profile fetch. Store ATProto tokens in the `oauth_sessions` table. Incorporate lessons from VS-1.
- V3-4: Platform JWT issuance. On successful ATProto auth, mint a platform JWT (RS256, same signing key from V0-5), set as HttpOnly cookie on `.robot.wtf`.
- V3-5: Signup flow. First-time users: after ATProto auth, prompt for platform username. Default to ATProto handle prefix (e.g., `sderle` from `sderle.bsky.social`, or domain prefix from a custom handle). Validate against reserved names and existing slugs. Create user record in SQLite.
- V3-6: Wire TenantResolver to validate platform JWTs from the cookie. Replace the hardcoded test auth from V1.
@@ 152,16 211,16 @@
**Goal:** Claude.ai can connect to a robot.wtf wiki via its standard MCP OAuth flow.
- This is the second big chunk of new work. It requires building a spec-compliant OAuth 2.1 Authorization Server.
+ VS-2 proved (or debugged) the protocol surface. This phase replaces the stub with production code: authlib's `AuthorizationServer`, real DCR persistence, ATProto-backed consent UI, proper token lifecycle.
**Tasks:**
- - V5-1: Implement OAuth 2.1 AS endpoints in the auth service. Dynamic Client Registration (`/auth/oauth/register`), authorization endpoint (`/auth/oauth/authorize`), token endpoint (`/auth/oauth/token`), AS metadata (`/.well-known/oauth-authorization-server`), JWKS (`/.well-known/jwks.json`).
+ - V5-1: Implement OAuth 2.1 AS using authlib's `AuthorizationServer` + `AuthorizationCodeGrant` (PKCE) + `ClientRegistrationEndpoint` (RFC 7591). Wire model callbacks against SQLite (`mcp_oauth_clients` table for DCR, authorization codes in a transient table or in-memory store). Incorporate findings from VS-2 about Claude.ai's specific requirements.
- V5-2: MCP protected resource metadata. Each wiki's MCP endpoint serves `/.well-known/oauth-protected-resource` pointing to robot.wtf's AS. Caddy routes this from the wiki subdomain to the MCP sidecar (or a small handler).
- V5-3: Authorization UI. The `/auth/oauth/authorize` endpoint renders a consent page. If the user is already logged in (platform JWT cookie): show "Authorize Claude to access {wiki}?". If not logged in: show "Sign in with Bluesky" which triggers the ATProto flow (V3) and then returns to consent.
- V5-4: Token issuance. The AS issues JWTs containing the user's DID and authorized wiki slug, signed with the platform RS256 key. MCP sidecar validates these the same way it validates platform JWTs.
- - V5-5: Test against Claude.ai. Add a wiki as an MCP server in Claude.ai settings, walk through the OAuth flow, verify tools work. Plan for a debugging cycle — Claude.ai's MCP OAuth client can be finicky (see open question #4 in [[Design/VPS_Architecture]]).
- - V5-6: Test against Claude Code. Verify `claude mcp add --transport http` with the OAuth flow works (Claude Code also supports OAuth discovery, not just bearer tokens).
+ - V5-5: Test against Claude.ai. Full end-to-end: add wiki MCP URL in Claude.ai, complete OAuth flow with real ATProto login, call wiki tools.
+ - V5-6: Test against Claude Code. Verify `claude mcp add --transport http` with OAuth discovery works (in addition to the existing bearer token path).
**Exit criteria:**
- Claude.ai can discover, authenticate, and use MCP tools on a robot.wtf wiki
@@ 221,16 280,19 @@
```
V0 (VM + Caddy + TLS)
+ ├─ VS-1 (ATProto OAuth spike)
+ ├─ VS-2 (MCP OAuth AS stub)
+
└─ V1 (Otterwiki on Caddy)
├─ V2 (Migrate dev wikis)
- └─ V3 (ATProto OAuth)
+ └─ V3 (ATProto OAuth — informed by VS-1)
└─ V4 (Management API)
- ├─ V5 (MCP OAuth AS)
+ ├─ V5 (MCP OAuth AS — informed by VS-2)
└─ V6 (Frontend + Landing)
└─ V7 (Semantic search + Ops)
```
- V2 (migration) and V3 (ATProto OAuth) can run in parallel after V1. V4 depends on V3 (needs real auth). V5 and V6 can run in parallel after V4. V7 is the final polish.
+ VS-1, VS-2, and V1 can all run in parallel after V0. The spikes don't block V1 — they're independent experiments on the same VM. V2 and V3 can run in parallel after V1. V4 depends on V3 (needs real auth). V5 and V6 can run in parallel after V4. V7 is the final polish.
**Minimum viable launch:** After V4, the service is usable — users can sign in with Bluesky, create a wiki, connect via Claude Code (bearer token), and browse the web UI. Claude.ai MCP OAuth (V5), the polished frontend (V6), and semantic search (V7) are improvements on a working service.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9