# Acequia Auth Infrastructure — Status & Tracking

> **Completed Project** — implemented. Now serves as platform reference.

**Last updated:** 2026-03-06
**Scope:** Token types, implementation parity, known gaps, next steps

---

## Token Type Inventory

| Type | Detection | Created By | Verified By | Status |
|------|-----------|------------|-------------|--------|
| **Chain tokens** | `parent` claim present | `acequia.chains.createChainToken()` | Server: `chains.mjs` resolveAndVerify | **Active** — Phases 1-3 |
| **User tokens** | `kid` claim, no `parent` | `acequia.tokens.createUserToken()` | Server: `jwtAuth.mjs` against user record | **Active** — depth-0 chains |
| **Legacy device tokens** | no `kid`, no `parent` | `acequia.tokens.createDeviceToken()` | Server: `jwtAuth.mjs` against device file | **Deprecated** |
| **Stored token refs** | CUID2 heuristic (no dots, 20-30 chars) | POST `/auth/stored-tokens` | Resolves to JWT, then one of above | **Active** |

### Token Flow: Request → Resolution → Verification → Authorization

```
Request with token (JWT or CUID2)
    │
    ├─ CUID2? → storedTokens.getJwtByTokenId() → resolve to JWT
    │           (check status, expiry, chainHash→chain store)
    │
    ├─ JWT with parent claim → chains.resolveAndVerify()
    │   Walk parent hash pointers root→leaf, verify:
    │   - Signatures at each link (delegator's public key)
    │   - Scope attenuation (child ⊆ parent)
    │   - Expiry attenuation (child.exp ≤ parent.exp)
    │   - Depth ≤ max_depth
    │   - Revocation list check at each link
    │   → effectiveScope
    │
    ├─ JWT with kid, no parent → jwtAuth.authenticateUserToken()
    │   Load user record, find key by kid, verify signature
    │   → user role + paths/writePaths
    │
    └─ JWT with no kid, no parent → jwtAuth.authenticateDeviceToken()
        Load device file, verify signature (DEPRECATED)

Authorization:
    Chain tokens → scope-based (effectiveScope.paths, writePaths)
    User tokens → role-based (owner/editor/viewer) + path-based
```

---

## Implementation Matrix: localWebDAV vs BrowserDAV

| Feature | localWebDAV (server) | BrowserDAV (browser) | Status |
|---------|---------------------|---------------------|--------|
| **Chain verification** | `chains.mjs` resolveAndVerify | `acequia.chains.resolveAndVerify()` | **Done** — both fully implemented |
| **Key resolution** | Filesystem: `auth/{sub}/users/{id}.json` | `createCombinedKeyResolver(authUrl)` — local + server fetch | **Done** |
| **Revocation checking** | `revocations.json` per-subdomain | `createWalletRevocationChecker()` — IndexedDB wallet | **Done** (wallet-only; server list not yet queried) |
| **Token creation** | Server stores chain + CUID2 leafId | Client creates chain, server assigns leafId on store | **Done** |
| **Scope enforcement** | `ScopedFileSystemAdapter.isAuthorized()` | `checkScopeAccess()` + `matchesPath()` | **Done** |
| **Scope-filtered listings** | `DirectoryJsonPlugin` wraps `getInternalMembers` | `_makeDotfileFilter` entry filter | **Done** |
| **Sidecar public access** | `jwtAuth.mjs` `checkSidecarAccess()` with fs cache | `main.js` `checkSidecarAccess()` with FS Access API | **Done** |
| **Dotfile filtering** | `ScopedFileSystemAdapter` + `DirectoryJsonPlugin` | `handleWebDAVRequest` pre-check | **Done** |
| **Owner bypass token** | N/A (server is always owner) | Per-session `crypto.randomUUID()` for local UI | **Done** |
| **Auth toggle** | Always on | Toggle in UI, persisted to localStorage | **Done** |
| **Token list/management** | Dashboard UI | Token list with copy/view/revoke | **Done** |
| **File browser** | Dashboard "File Browser" tab | Embedded `<acequia-file-browser>` section | **Done** |
| **Short token IDs** | CUID2 `leafId` on chain store | Same — `leafId` display + resolution | **Done** |
| **User token routing** | Full (kid → user record lookup) | Not handled — chains only | By design |
| **Legacy device tokens** | Supported (deprecated) | Not supported | By design |
| **Role-based access** | owner/editor/viewer with path grants | Scope-only from chain | By design |
| **Token revocation** | Server revocation list + key status | Local wallet only | Server list not checked by browser |

### Resolved Gaps

The following were previously P0 blockers — all now implemented:

1. **Client-side chain verification** — `resolveAndVerify()`, `resolveChain()`, `verifyChain()` all implemented in `acequia2/auth/chains.js`. BrowserDAV performs full cryptographic chain verification with graceful fallback to scope-only decoding.

2. **Key resolution factories** — `createCombinedKeyResolver(authUrl)` tries local keys first, then fetches from server at `GET /auth/users/{userId}/keys/{kid}/public`. `createLocalKeyResolver()` and `createServerKeyResolver(authUrl)` also available.

3. **Revocation checking** — `createWalletRevocationChecker()` checks IndexedDB wallet for revocation status. Covers tokens the local user created and revoked.

---

## Phase Tracker

### Phase 1: Enrich Tokens — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| `parent`, `depth`, `max_depth` JWT claims | `acequia2/auth/chains.js` | ✓ |
| Three-mode detection in authenticator | `src/auth/jwtAuth.mjs:103-109` | ✓ |
| Backward compat (existing tokens unchanged) | `src/auth/jwtAuth.mjs` | ✓ |

### Phase 2: Chain Verification — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| Content-addressable chain store | `src/chains.mjs` (storeToken, loadToken) | ✓ |
| Chain resolution (walk parent hashes) | `src/chains.mjs` (resolveChain) | ✓ |
| Chain verification (sigs, scope, depth, expiry) | `src/chains.mjs` (verifyChain) | ✓ |
| Scope attenuation enforcement | `src/chains.mjs` (isScopeSubset) | ✓ |
| Revocation list | `src/revocations.mjs` | ✓ |
| 53 server-side tests | `test/*.test.mjs` | ✓ (60/61, 1 flaky ENOTEMPTY) |

### Phase 3: Client-Side Minting — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| Browser SHA-256 hash (crypto.subtle) | `acequia2/auth/chains.js` (hashToken) | ✓ |
| Root + scoped token creation | `acequia2/auth/chains.js` (createRootToken, createScopedToken) | ✓ |
| Chain storage via PUT | `acequia2/auth/chains.js` (storeChainToken) | ✓ |
| IndexedDB wallet CRUD | `acequia2/auth/chains.js` (list/get/update/revoke/removeWalletToken) | ✓ |
| Scope verification utilities | `acequia2/auth/chains.js` (isScopeSubset, checkScopeAccess, matchesPath) | ✓ |
| Dashboard chain token UI | `public/dashboard.html` | ✓ |
| BrowserDAV token creation UI | `acequia/{sub}/BrowserDAV/main.js` | ✓ |

### Phase 3.5: Client-Side Verification — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| Client `resolveAndVerify()` | `acequia2/auth/chains.js` | ✓ |
| Client `resolveChain()` (HTTP fetch) | `acequia2/auth/chains.js` | ✓ |
| Client `verifyChain()` (jose verify) | `acequia2/auth/chains.js` | ✓ |
| `createCombinedKeyResolver()` factory | `acequia2/auth/chains.js` | ✓ |
| `createServerKeyResolver()` factory | `acequia2/auth/chains.js` | ✓ |
| `createLocalKeyResolver()` factory | `acequia2/auth/chains.js` | ✓ |
| `createWalletRevocationChecker()` factory | `acequia2/auth/chains.js` | ✓ |
| BrowserDAV request auth working | `acequia/{sub}/BrowserDAV/main.js` | ✓ |
| Graceful fallback to scope-only | `acequia/{sub}/BrowserDAV/main.js` | ✓ |

### Phase 3.6: Sidecar Public Access + Dotfile Filtering — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| `.acequia-access.json` sidecar spec | Design: `documents/projects/public-access.md` | ✓ |
| Server-side sidecar access | `src/auth/jwtAuth.mjs` (`checkSidecarAccess`) | ✓ |
| BrowserDAV sidecar access | `acequia/{sub}/BrowserDAV/main.js` (`checkSidecarAccess`) | ✓ |
| Sidecar walk-up-ancestors with caching | Both servers (60s TTL on server, in-memory on browser) | ✓ |
| Child directory scanning | `jwtAuth.mjs` (`_scanChildPublicPaths`) | ✓ |
| Dotfile write-gated filtering (server) | `src/serverFactories.mjs` (`ScopedFileSystemAdapter`) | ✓ |
| Dotfile filtering in listings (server) | `src/plugins/directoryJson.mjs` (`DirectoryJsonPlugin`) | ✓ |
| Dotfile filtering (BrowserDAV) | `acequia/{sub}/BrowserDAV/main.js` (`handleWebDAVRequest`) | ✓ |
| `DOTFILE_READER_ALLOWLIST` | Both: `['.well-known', '.ai']` | ✓ |
| Scope-filtered directory listings | `directoryJson.mjs` + `ScopedFileSystemAdapter` | ✓ |

### Phase 3.7: File Browser UI — COMPLETE ✓

| Deliverable | File | Works E2E? |
|-------------|------|------------|
| `<acequia-file-browser>` custom element | `public/components/acequia-file-browser/` (7 files) | ✓ |
| Tree + list dual-pane navigation | `file-tree.js` + `file-list.js` | ✓ |
| Access indicator badges | `access-resolver.js` | ✓ |
| Sidecar editor (visual rule editing) | `sidecar-editor.js` | ✓ |
| CSS custom properties theming | `styles.js` (`--fb-*` variables) | ✓ |
| Dashboard integration | `public/dashboard.html` + `dashboard.js` | ✓ |
| BrowserDAV integration | `acequia/{sub}/BrowserDAV/index.html` + `main.js` | ✓ |
| Owner bypass token for sidecar editing | `acequia/{sub}/BrowserDAV/main.js` | ✓ |

### Phase 4: Peer Verification — IN DESIGN

- Peers verify chains during WebRTC handshake
- Chain bundling in data channel establishment
- Peer public key discovery via WebDAV namespace

### Phase 5: Policy Engine — IN DESIGN

- Declarative policy rules for automatic delegation
- Policy documents in WebDAV namespace

---

## Known Gaps (Prioritized)

### P0 — None

All previously-P0 blockers (client-side chain verification, key resolution factories, BrowserDAV auth) are resolved.

### P1 — Consistency

1. **Server revocation not checked by browser** — BrowserDAV only checks local wallet revocation. A token revoked on the server via the dashboard is still accepted by BrowserDAV until the browser session is refreshed. Fix: add `fetchRevocationList()` that checks `GET /auth/revocations` (may need a public revocation-check endpoint).

2. **Root JWT not in wallet** — Client wallet stores only `leafJwt`. Can't reconstruct full chain for re-delegation without fetching root from server.

### P2 — Deploy & Testing

3. **Commit & deploy** sidecar access, file browser component, and dotfile filtering changes (currently uncommitted on `ft-acequia-apps`).

4. **End-to-end sidecar editing** — Test the BrowserDAV owner bypass token flow: file browser → bypass token → write `.acequia-access.json` → access rules take effect.

5. **Anonymous user navigation** — Verify filtered directory listings on both servers: ancestor directories show only publicly-accessible children.

### P3 — Cleanup

6. **Legacy device token sunset** — No timeline set. Device token code path still active in `jwtAuth.mjs`.

7. **Token renewal creates new entries** — Dashboard creates new stored token on refresh instead of updating existing one's JWT.

---

## Open Questions (from capability-delegation.md)

| # | Question | Status |
|---|----------|--------|
| 1 | Refresh protocol — how do delegated token holders request refresh? | **Open** |
| 2 | Cross-subdomain delegation | **Deferred** |
| 3 | Policy language format (Phase 5) | **Deferred** |
| 4 | Isomorphic verification module vs parallel implementations? | **Decided:** Parallel — server uses Node crypto, client uses crypto.subtle. Same algorithm, different APIs. Shared test vectors recommended. |
| 5 | Non-owner root chains — should editors create root chains? | **Open** — currently allowed, may break single-root-authority model |
| 6 | App identity — what is a BrowserDAV instance's identity? | **Partially answered** — uses the running user's keys, but relationship to group and node server auth namespace needs design |
| 7 | Public access mechanism? | **Decided:** Sidecar files (`.acequia-access.json`) — implemented in both servers. Nearest-wins inheritance, walk-up-ancestors, deny patterns. See `public-access.md`. |
| 8 | Dotfile visibility? | **Decided:** Write-gated — dotfiles visible only to users with write access. `DOTFILE_READER_ALLOWLIST` for exceptions. Implemented in both servers. |
| 9 | File browser for BrowserDAV? | **Decided & implemented:** `<acequia-file-browser>` custom element with sidecar editing. Integrated in both dashboard and BrowserDAV. |

---

## Related Documents

| Document | Location | Purpose | Accuracy |
|----------|----------|---------|----------|
| capability-delegation.md | `projects/` | Master architecture + migration plan | Current — updated 2026-03-06 |
| public-access.md | `projects/completed/` | Sidecar access design + implementation | Current — updated 2026-03-06 |
| filesystem-browser-ui.md | `projects/` | File browser component design | Current — updated 2026-03-06 |
| user-authentication.md | `platform/` | JWT architecture reference | Needs update for sidecar access |
| localWebDAV-auth-analysis.md | `archive/` | Original design spec | Historical — pre-chain |
| CLAUDE.md | `.` | Project map | Current |
