Acequia Chain-Tokens and the OAuth Standard (9242fee3)

**Note** from Bead: 9242fee3 · [canonical source](https://redfish.acequia.io/guerin/.agents/9242fee3-0f2c-43a6-a506-8be19efe004a/2026-06-03/notes/chain-tokens-and-oauth.md) · session 2026-06-03 · discussion: Talk: 9242fee3

**Question (Stephen, 2026-06-03):** Explain acequia chain-tokens and their relationship to the OAuth standard. Is the chain-token design consistent with OAuth? **Short answer:** > Acequia chain-tokens are **wire-compatible with the OAuth 2.0 + JWT ecosystem** (RFC 6749, 7519, 7515, 6750) — same JWT format, same `Authorization: Bearer` presentation, same JOSE algorithms, overlapping standard claims. But the **trust topology is fundamentally different** in two ways: (1) the **cryptographic trust root** is the user's own keypair (self-sovereign), not an AS's signing key; (2) the **operational AS-role** — token-granting URIs like `/auth/delegate`, `/auth/invites`, `/auth/chains` — *exists* but is served by a **decentralized peer mesh** (service workers, discovery servers, local OS-level acequia node servers — "origins" in Cloudflare-tunnel speak, generalized) with eventual-consistency shared state, leader election, and load balancing, rather than a single centralized AS deployment. No client registration. No grant-type flows. The chain-token model belongs to the **capability-family** of auth systems (UCAN, Macaroons, Biscuits, ZCAP-LD) that borrow OAuth's transport but replace its centralized broker with **bearer-verifiable, peer-mediated delegation chains over a mesh-served namespace**. Within OAuth proper, the closest cousin is **RFC 8693 OAuth 2.0 Token Exchange** — but RFC 8693 still routes through an AS deployment, while acequia routes through a URI served by a peer mesh. This note unpacks that answer.

## 1. What an acequia chain-token actually is Anchored in canonical docs (read-only sources for this bead): - [`acequia.io/documentation/platform/user-authentication.md`](https://acequia.io/documentation/platform/user-authentication.md) — JWT structure, three-mode detection, verification - [`acequia.io/documentation/platform/projects/capability-delegation.md`](https://acequia.io/documentation/platform/projects/capability-delegation.md) — design rationale, Phases 1–3.7 implemented, Phase 4+ in design - [`acequia.io/documentation/platform/webdav-server.md`](https://acequia.io/documentation/platform/webdav-server.md) — server-side enforcement, three-mode routing ### Anatomy of a chain A chain is a linked list of JWTs, walked from leaf to root via SHA-256 `parent` pointers. ``` JWT_0 (root grant) JWT_1 (delegated) JWT_2 (further delegated) sub: alice iss: alice iss: bob iss: subdomain sub: bob sub: carol kid: <alice's JWK thumbprint> kid: <alice's thumbprint> kid: <bob's thumbprint> paths: [/docs/*] parent: sha256(JWT_0) parent: sha256(JWT_1) writePaths: paths: [/docs/shared/* paths: [/docs/shared/*] exp: 2026-07-01 writePaths: [] writePaths: [] depth: 1 depth: 2 max_depth: 3 max_depth: 3 exp: ≤ JWT_0.exp exp: ≤ JWT_1.exp (signed by alice) (signed by bob) ``` Each link is a normal JWS (PS256 / RFC 7515). The chain-specific information lives in three custom claims: | Claim | Purpose | |---|---| | `parent` | SHA-256 hex of the parent JWT's compact serialization. Forms the chain. | | `depth` | This token's position in the chain (0 = root). | | `max_depth` | Maximum allowed chain length. Each link must respect it. Default 3. | Scope lives in `paths` (read) and `writePaths` (write) — glob-style WebDAV path matchers (`*`, `/prefix/*`, exact). ### Storage Content-addressable on the server: `/auth/{subdomain}/chains/{sha256}.jwt` — the filename is the SHA-256 hex of the JWT string itself. Each link stored once. The chain isn't bundled into a single artifact; the leaf carries `parent` and the server walks the chain by fetching parent hashes. Client-side: each browser node keeps a wallet of held chains in IndexedDB (`acequia-chains` localforage instance). ### Verification (server, [`src/chains.mjs`](https://acequia.io/documentation/platform/projects/capability-delegation.md)) Given a leaf JWT: 1. **Resolve.** Walk `parent` hashes from leaf to root, loading each JWT from the chain store. 2. **Verify root.** Check `sub` is a registered user. Find their public key by `kid` (RFC 7638 thumbprint). Verify the signature. Check the key is not revoked. 3. **Verify each link.** For each child: - `parent` hash matches the previous JWT - `depth` increments and stays ≤ `max_depth` - Child scope ⊆ parent scope (`isScopeSubset`) — **attenuation only, no escalation** - Child `exp` ≤ parent `exp` - Signature verifies against delegator's public key - Not on revocation list 4. **Return effective scope** from the leaf. This walk happens on every request that presents a chain token. The browser-side equivalent (`acequia2/auth/chains.js`) uses `crypto.subtle` with the same algorithm. ### Three-mode detection ([`jwtAuth.mjs`](https://acequia.io/documentation/platform/user-authentication.md)) The server classifies tokens by claim inspection: ```javascript if (decToken.parent) { return authenticateChainToken(...) // chain (new, going forward) } else if (decToken.kid) { return authenticateUserToken(...) // user token (depth-0 chain, structurally) } else { return authenticateDeviceToken(...) // legacy device token (deprecated) } ``` A standalone user token (no `parent`) is, by design, a **depth-0 chain**. The chain machinery is purely additive — existing user tokens keep working unchanged.

## 2. OAuth 2.0 + JWT, just enough to compare OAuth 2.0 (RFC 6749) defines four roles: | Role | What it does | |---|---| | **Resource Owner** | The user who owns the resource. Grants access. | | **Client** | The third-party app the user wants to authorize. | | **Authorization Server (AS)** | Issues access tokens. *Centralized broker.* | | **Resource Server (RS)** | Hosts the resource, accepts tokens. | The protocol describes flows ("grant types") by which a client obtains an access token from the AS *on behalf of* the resource owner: authorization code (with PKCE), client credentials, device code, refresh token, etc. JWT access tokens (RFC 9068) are the dominant access-token format today: signed JWS, standard claims (`iss`, `sub`, `aud`, `exp`, `iat`, `jti`, `scope`), verified by the RS without a round-trip to the AS (no RFC 7662 introspection needed) provided the RS knows the AS's public keys (via JWKS). Bearer presentation: `Authorization: Bearer <token>` (RFC 6750). Delegation in OAuth-land: **RFC 8693 OAuth 2.0 Token Exchange**. A client presents a `subject_token` (and optionally an `actor_token`) to the AS and asks for a downstream token with narrower scope or a different audience. The AS mints the new token. The chain of who-acted-on-whose-behalf is recorded in `may_act` / `act` claims. *Still AS-mediated.* The downstream token is a fresh JWT signed by the AS, not a continuation of the subject token. Revocation: RFC 7009 (revoke endpoint at the AS) or short-TTL + refresh.

## 3. Wire-level compatibility — what's identical Acequia chain-tokens are **structurally valid OAuth-style JWT access tokens**. Specifically: | Aspect | OAuth/JOSE standard | Acequia chain-token | Match? | |---|---|---|---| | Token format | JWS Compact (RFC 7515) | JWS Compact | ✓ identical | | Signing algorithm | RS256, ES256, PS256, EdDSA | PS256 | ✓ standard JOSE alg | | Bearer presentation | `Authorization: Bearer …` (RFC 6750) | Same, plus `?token=` query and `auth_token` cookie | ✓ + extensions | | `iss` claim (RFC 7519) | Token issuer | Subdomain hostname (root) or delegating user (chain links) | ✓ | | `sub` claim | Subject | userId of the token's holder | ✓ | | `iat` / `exp` claims | Issued-at / expiry | Same semantics | ✓ | | `kid` header | RFC 7638 JWK Thumbprint | RFC 7638 JWK Thumbprint | ✓ identical convention | | Public key lookup | JWKS (RFC 7517) at well-known URL | `GET /auth/users/{userId}/keys/{kid}/public` returns JWK | ✓ functionally equivalent, different URL pattern | | Private claims | Allowed (RFC 7519 §4.3) | `parent`, `depth`, `max_depth`, `paths`, `writePaths` | ✓ standards-compliant use of private claim space | | Verification without AS round-trip | RFC 9068 inline JWT verification | Server walks chain, verifies each link inline | ✓ same "no-introspection" stance | A vanilla OAuth resource server that knows the issuer's public key can **structurally verify** an acequia chain-token's leaf signature. What it can't do without additional code is enforce the chain semantics — but that's not a violation of the standard, it's a layered protocol on top of valid JWS. In other words: **the wire format is OAuth-clean.** If you point a JOSE library at an acequia chain-token, it parses, the signature verifies, the standard claims look normal. The chain logic is an extension *expressed in private claims*, exactly where RFC 7519 §4.3 says private claims should live.

## 4. Trust-topology divergence — what's different The model **diverges from OAuth where it counts most**: in *who mints tokens, who decides authorization, and how delegation propagates*. ### 4.1 The AS-role is a URI served by a peer mesh, not a centralized service OAuth is built around an authorization server that mints access tokens. The AS is *operationally* one logical service — typically one HTTPS deployment (possibly load-balanced across replicas) with one trust root and one signing-key set. The whole grant-type ecosystem (code, PKCE, client credentials, device code, refresh) exists to bridge the client to that AS deployment in different contexts. Acequia **separates two things OAuth conflates**: 1. **Cryptographic trust root.** In OAuth: the AS's signing key. In acequia: the **user's own keypair**, generated per-device, never transferred. Root tokens are self-signed by the user; delegated tokens are signed by the delegating user. The trust root is genuinely AS-less in the OAuth sense — this is the self-sovereign property. 2. **Operational AS-role.** OAuth's AS also handles storage, lookup, delegation orchestration, key discovery, revocation distribution. Acequia *does* have token-granting URIs that handle these — `POST /auth/delegate` (server-assisted minting), `POST /auth/invites` (user provisioning with capability), `POST /auth/chains` (chain link storage), `POST /auth/device-link/accept` (key endorsement), `GET /auth/users/{id}/keys/{kid}/public` (public key discovery), `GET/POST /auth/revocations` (revocation distribution). These ARE token-granting endpoints in the AS sense — but the **URI is served by a decentralized peer mesh**, not a centralized deployment. The mesh, per the **Plan 9 Auth Namespace Overlay** section of [`capability-delegation.md`](https://acequia.io/documentation/platform/projects/capability-delegation.md) and the [`uri-bind-mount`](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md) frame: - **Service workers** in browsers act as in-tab ingress routers, mounting auth slices into their per-tab namespace (`acequiaMounts`) and routing auth requests to the appropriate peer - **Discovery servers** advertise and route to peer origins - **Local OS-level acequia node servers** (Cloudflare-tunnel "origins," generalized) serve auth namespace slices for the subdomains they hold - **Shared state** (chain store, user records, revocation lists) replicates across peers with **eventual consistency** - **Leader election** and **load balancing** across mesh peers — any peer can be the authority for a given request as long as it has the relevant state In Cloudflare-tunnel terms: where Cloudflare routes a URL to one or more origins, acequia generalizes this so the *origin set* is a decentralized peer mesh that any participant can join, with auth-namespace mounting (`/auth/{subdomain}/...`) as a first-class composition primitive. The "ingress router" is itself decentralized — running on service workers, discovery servers, and local node servers — not a single load-balancer in front of an AS deployment. This rearranges the OAuth role table: | OAuth concept | Acequia equivalent | |---|---| | Resource Owner | User (root of chain) | | Authorization Server — cryptographic trust root | User's own keypair — self-sovereign, per-device | | Authorization Server — operational service | Token-granting URIs (`/auth/delegate`, `/auth/invites`, `/auth/chains`, …) **mounted by a decentralized peer mesh** with eventual-consistency shared state, leader election, and load balancing | | Resource Server | acequia/Nephele WebDAV peer (also mesh-served via the same namespace overlay) | | Client | Whoever holds a delegated token (a user, an agent, a peer browser, an automated process) | ### 4.2 No client registration, no client_id, no client_secret OAuth's `client_id` / `client_secret` (RFC 6749 §2.3) identifies the *application* asking for access on the user's behalf. Dynamic Client Registration (RFC 7591) automates client provisioning. Either way, the AS knows which app is talking. Acequia has no notion of "client app" in the protocol. A delegated token holder is just *whoever holds the token* — could be a person, a browser tab, a CLI script, another acequia peer, an automated agent. The cryptographic question is "is this chain valid?", not "is this client registered?" Per the [agent-as-bead](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md) frame and the [URI bind/mount](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md) substrate, this is by design — the "client" is dissolved into "any holder of a capability." This is closer to **object-capability** discipline than OAuth: *possession of the reference (the token) is the permission*. ### 4.3 Delegation is bearer-verifiable, not AS-mediated OAuth delegation = RFC 8693 Token Exchange. The client sends `subject_token` to `/token` endpoint, asks for a downstream token, the AS decides whether to mint it and signs the result. The downstream JWT is signed by the AS; the chain of delegation is recorded in `act` / `may_act` claims but the AS is the cryptographic root. Acequia delegation is **peer-to-peer**. Alice has a token. Alice mints a child token, signing it with her own key, encoding the parent's hash in the `parent` claim. She hands the child token to Bob. Bob can present it to any acequia node that knows Alice's public key — no AS involved. Bob can further delegate to Carol, signing with his own key. The chain is fully verifiable from the leaf without phoning home to any central authority. This is the **defining capability-system property**: the holder of a capability can attenuate and delegate it without consulting any third party. ### 4.4 Attenuation is enforced structurally

OAuth scope is "what the AS granted you." If you want a smaller scope, you ask the AS for a new token. There's nothing in the protocol preventing the AS from issuing a downstream token with *wider* scope than the subject token — RFC 8693 leaves that to policy. Acequia attenuation is **a property of the verification algorithm**. Every link's scope must be a subset of its parent (`isScopeSubset`). Every link's `exp` must be ≤ its parent's. `depth` ≤ `max_depth`. A token can never delegate more than it has — and this isn't policy, it's the check the verifier always performs. Escalation is structurally impossible. The acequia metaphor maps directly: | Acequia water tradition | Chain-token system | |---|---| | Acequia madre (mother ditch) | Root grant — the user's full scope | | Mayordomo allocates flow to parciantes | Owner delegates subsets to other users | | Parciante cannot exceed allocation | `isScopeSubset` blocks escalation | | Closing the headgate | Non-renewal of short-lived child token | | Tracing water rights back | Walking the chain to the registered user | ### 4.5 Refresh becomes "headgate," not refresh tokens OAuth refresh tokens (RFC 6749 §6) are long-lived credentials presented to the AS in exchange for a fresh access token. The AS decides whether to honor the refresh (revocation, policy, user disablement). Acequia uses the **headgate pattern** (Design Decision #4 in `capability-delegation.md`): tiered TTL. Root tokens long-lived (~30 days). Delegated tokens short-lived (depth-1 ~1–4 hours, depth-2 ~15–60 minutes). To keep a delegatee active, the delegator re-issues a fresh child token. To revoke, *they stop re-issuing*. The capability withers naturally. This isn't an AS withholding a refresh; it's the delegating peer deciding not to mint another link. Acequia also keeps an explicit revocation list at `/auth/{subdomain}/revocations.json` (belt + suspenders) for immediate-effect cutoff of compromised tokens. That's loosely analogous to OAuth token revocation (RFC 7009), but it's a *pulled* well-known JSON document rather than a push to the AS revoke endpoint. ### 4.6 No grant-type flows OAuth's flows (authorization code, device code, client credentials) exist to bootstrap a client into possession of a token from an AS the user trusts. Each handles a specific deployment context (browser-based apps, headless devices, server-to-server). Acequia has **no equivalent**. The closest flows are: - **Device link** — an existing device on a user's account signs a short-lived token that lets a new device register a key under the same user. Conceptually adjacent to OAuth device code grant + dynamic client registration, but operates *user-to-user-same-identity*, not client-to-AS. - **Invite** — the subdomain owner mints a JWT (currently CUID2-keyed JSON, not a chain token yet — see "Option A vs B" in `capability-delegation.md`) that lets a new user register with pre-assigned permissions. Adjacent to OAuth dynamic registration with pre-configured grants. These are user-provisioning mechanisms, not token-issuance flows in the OAuth sense.

## 5. Side-by-side mapping table The whole comparison in one place: | Dimension | OAuth 2.0 + JWT (RFC 6749 / 7519 / 8693) | Acequia chain-tokens | Verdict | |---|---|---|---| | Token format | JWS Compact | JWS Compact | ✓ same | | Signing alg | RS256 / ES256 / PS256 / EdDSA | PS256 | ✓ standard subset | | Bearer presentation | `Authorization: Bearer` (RFC 6750) | `Authorization: Bearer` + `?token=` + cookie | ✓ + extensions | | Standard claims used | `iss, sub, aud, exp, iat, nbf, jti, scope` | `iss, sub, exp, iat, kid` | ✓ subset, all RFC 7519 | | Private claims | Allowed | `parent, depth, max_depth, paths, writePaths` | ✓ standards-compliant | | Cryptographic trust root | Authorization Server's signing key | User's own keypair (self-sovereign, per-device) | ✗ divergent | | Operational AS-role (storage, lookup, revocation, delegation orchestration, key discovery) | Single logical AS deployment | Token-granting URI served by **decentralized peer mesh** (service workers, discovery servers, local node servers) with eventual-consistency state, leader election, load balancing | ✗ divergent topology | | Token issuer (`iss`) | The AS | The user (root) or delegating user (links) | ✗ different referent | | Client identity | `client_id` + `client_secret` / mTLS / DPoP | None — possession-based | ✗ no analogue | | Grant types | Code, PKCE, device, client-creds, refresh | None — direct user signing | ✗ no analogue | | Delegation | RFC 8693 Token Exchange (AS-mediated) | `parent`-hash chain (peer-mediated) | ✗ **fundamentally different** | | Attenuation guarantee | Policy-level at AS | Structural: `isScopeSubset` per link | ✗ stronger | | Scope language | Space-separated strings | Path glob arrays | ≈ analogous | | Refresh model | Refresh token → AS → new access token | Headgate: delegator re-issues short-lived child | ✗ structurally different | | Revocation | RFC 7009 revoke endpoint, introspection | `/auth/{subdomain}/revocations.json` + non-renewal | ≈ analogous, different topology | | Verifier needs | AS's JWKS | Root user's public key + all intermediate signers' keys | ✗ chain traversal | | Offline verification | Yes with JWKS cache | Yes with key-resolver cache | ✓ same property | | Cross-domain federation | Resource indicators (RFC 8707), trusted issuer lists | Plan9-style auth namespace overlay (designed, not built) | ≈ different mechanism | | Audit chain | `act` / `may_act` (RFC 8693) | Content-addressable chain in `/auth/.../chains/{sha256}.jwt` | ≈ analogous, different storage | **Pattern of the table:** transport and crypto are the same; the trust topology, the issuer model, and the delegation mechanism are different. Acequia uses OAuth's tools and discards OAuth's centralized broker.

## 6. The closest OAuth cousin: RFC 8693 Token Exchange Of the OAuth specs, RFC 8693 OAuth 2.0 Token Exchange is closest in *intent* to acequia chain-tokens. Both address: "I have a token, how do I produce a narrower-scoped token to pass downstream?" The differences are instructive: | | RFC 8693 Token Exchange | Acequia chain-token | |---|---|---| | Who mints downstream | Authorization Server | Holder of the parent token | | Signing key | AS's key | Holder's own key | | Chain visibility | `act` / `may_act` claims (semantic) | `parent` hash + signature (cryptographic) | | Offline mint | No (AS required) | Yes | | Offline verify | Yes (with JWKS) | Yes (with key resolvers) | | Attenuation | Policy at AS | Structural at verifier | | Re-delegation depth | Implicit — depends on AS policy | Explicit — `max_depth` claim | RFC 8693 is the *centralized version of the same idea*. If acequia ever wanted to bridge to OAuth-deployed systems, RFC 8693 would be the natural bridge: an acequia-aware AS could mint OAuth tokens that mirror the leaf of a chain, with `act` claims encoding the chain history. That's federation territory, not in scope today.

## 7. Family resemblance: capability-systems Acequia chain-tokens are not the first auth system to take this shape. The family: | System | Mechanism | Relationship to acequia | |---|---|---| | **UCAN** (Fission / WNFS / IPFS) | Chained JWTs with attenuation, holder mints sub-tokens, verification walks chain to root | **Closest cousin.** Acequia chain-tokens are essentially UCAN with PS256 + WebDAV-path scopes + content-addressable chain storage. The "Prior Art" table in `capability-delegation.md` lists UCAN first. | | **Macaroons** (Google, 2014) | HMAC-chained cookies, anyone in chain can append a "caveat" that narrows scope | Same attenuation property, different crypto. Macaroons use shared-key HMAC chains; acequia uses public-key JWS chains. JWS gives third-party verifiability without sharing secrets. | | **Biscuits** (Clever Cloud) | Signed token + Datalog policy language for caveats | Same shape, more expressive policy. Acequia's path-glob scope language is much simpler than Datalog; could be a future direction. | | **ZCAP-LD** (Digital Bazaar, W3C) | Linked Data capabilities with delegation chains | Different serialization (JSON-LD vs JWS) but identical capability discipline. ZCAP-LD bridges into the W3C Verifiable Credentials ecosystem. | | **Verifiable Credentials** (W3C) | Issuer→Holder→Verifier with wallet-mediated presentation | More general framework; capability delegation is one VC use case. Acequia could expose chain-tokens as VCs for cross-ecosystem interop. | | **Object Capabilities (ocap)** (E lang, Capnp) | Unforgeable references; possession = permission | The conceptual root of the whole family. Acequia chain-tokens are ocap-discipline expressed in JWS. | | **SPIFFE / SPIRE** | Workload identity with auto-issued certs from policy | Different problem (workload identity, not delegation) but adjacent — SPIFFE could plausibly issue acequia root tokens for service workloads. | The whole family says: **"a token IS the capability."** No external lookup, no AS round-trip, no role table on the server. The token's signature, scope, and (where applicable) chain are sufficient to authorize. OAuth in its base form (RFC 6749) does *not* say this — OAuth says "the AS decides; the RS asks (introspection) or trusts the AS-signed JWT." JWT access tokens (RFC 9068) push toward capability semantics but still terminate at the AS as the trust root. **Capability systems push the trust root all the way down to the user.**

## 8. So — is it consistent with OAuth? Depends what "consistent" means. **Consistent at the wire format:** ✓ Yes. The JWT is a valid JWS. Standard claims have standard meanings. A JOSE library will parse and signature-verify it without issue. An OAuth RS that knows the issuer's public key can validate the leaf signature — it just won't enforce chain semantics it doesn't know about. **Consistent with the OAuth trust model:** ✗ No — but not because acequia "has no AS." OAuth presupposes an authorization server as both the cryptographic trust root *and* the operational token-granting service. Acequia separates these: the cryptographic root is the user's own keypair (self-sovereign), and the operational AS-role lives at URIs served by a decentralized peer mesh (Plan-9-style auth-namespace overlay — service workers as in-tab ingress routers, discovery servers, local OS-level node servers as Cloudflare-tunnel "origins" generalized; eventual-consistency shared state, leader election, load balancing). The two architectures *can interoperate at the boundary* via RFC 8693-style bridges, but the inner topologies are different. **Consistent with where modern OAuth is heading:** ≈ Partly. The trend toward JWT access tokens (RFC 9068), no-introspection inline verification, short-lived tokens with refresh, DPoP-style holder-binding, and capability-flavored scopes (Rich Authorization Requests, RFC 9396) all move OAuth *closer to* the capability family. Acequia is OAuth's neighborhood, just on the other side of the "is there an AS?" question. If the OAuth ecosystem keeps moving in the capability direction, the gap narrows. **Consistent with the rest of the Acequia design (Hubler-net cognition, agent-as-bead, URI bind/mount, advanced-wave accounting):** ✓ Yes, deeply. Capability discipline is what makes peer-mediated delegation possible — and peer-mediated delegation is what makes the [downstream-pattern](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/downstream-pattern.md), [polarized-links](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/polarized-links.md), and [paths-as-event-bus](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/paths-as-event-bus.md) frames work. An AS in the middle would break the topology. **Verdict (one sentence):** Acequia chain-tokens are *wire-compatible* with OAuth + JWT but belong to a *different architectural family* (capabilities, UCAN-style) — borrowing OAuth's transport while replacing its centralized issuer with peer-mediated, bearer-verifiable delegation chains.

## 9. Open questions for future federation These are the places where the OAuth question becomes operationally interesting: 1. **External system interop.** When an external service that speaks OAuth wants to grant access to an acequia resource (or vice versa), what bridge? RFC 8693 with an acequia-aware AS that wraps chain leaves? OIDC discovery exposing acequia public keys as JWKS? Verifiable Credentials presentation? *Capability-delegation.md* defers this as "when external system interop becomes real" — DID adoption (`did:acequia:{cuid2}`) is the proposed seam. 2. **JWKS exposure.** `GET /auth/users/{userId}/keys/{kid}/public` returns the JWK for a single key. An OAuth client looking for "the issuer's keys" expects a JWKS document (a `keys` array) at `/.well-known/jwks.json` per RFC 8414 metadata. Trivial to add a JWKS aggregation endpoint that lists all registered users' active keys — but is that the right granularity? Per-user JWKS at `/.well-known/jwks-{userId}.json`? 3. **`aud` claim.** Acequia tokens currently don't use `aud` (the `capability-delegation.md` sketch shows `aud: "/project/maps"` but the implemented payload doesn't include it; scope lives in `paths`/`writePaths`). For OAuth interop, `aud` would identify the resource server. For multi-instance federation (Phase 4 peer verification), `aud` could disambiguate which acequia peer's namespace the token is for. 4. **DPoP-style holder binding.** OAuth DPoP (RFC 9449) binds an access token to a per-request proof-of-possession key, preventing stolen-token replay. Acequia chain-tokens currently are pure bearer — possession = permission. For high-value scopes (root tokens, long-lived), holder-binding could harden against token theft without breaking the capability discipline. 5. **Mapping chain `iss` → OAuth `iss`.** In acequia, root tokens use `iss: <subdomain hostname>` and chain links use `iss: <delegating userId>`. An OAuth verifier sees changing `iss` values within a single delegation chain — fine for capability semantics, surprising for OAuth-centric tooling. A bridge layer might normalize this. 6. **Key rotation crossing the OAuth boundary.** Gap 1 in `capability-delegation.md` (no key-rotation workflow yet) becomes urgent for OAuth interop: external systems will expect standard JWKS rotation semantics with `keyStatus: active|rotating|retired` and a grace period. 7. **Scope language at the OAuth boundary.** OAuth scope strings are opaque to the protocol. Acequia path-glob scope is semantically rich but doesn't round-trip through OAuth scope strings cleanly. RFC 9396 (Rich Authorization Requests) provides a JSON-typed scope channel that *would* round-trip cleanly. Worth tracking when chain-tokens want to bridge.

**See also:** - Canonical platform docs: [`user-authentication.md`](https://acequia.io/documentation/platform/user-authentication.md), [`capability-delegation.md`](https://acequia.io/documentation/platform/projects/capability-delegation.md), [`webdav-server.md`](https://acequia.io/documentation/platform/webdav-server.md) - Acequia vocabulary entry for "Chain-Token": [`9e1d87f5.../user-representation-vocabulary.md`](https://redfish.acequia.io/guerin/.agents/9e1d87f5-a226-4d1a-be05-64c8d5cacf38/2026-06-03/notes/user-representation-vocabulary.md) - Design substrate this assumes: [`agent-as-bead`](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/agent-as-bead.md), [`uri-bind-mount`](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/uri-bind-mount.md), [`downstream-pattern`](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/downstream-pattern.md), [`apoptosis-vs-necrosis`](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/2026-04-23/notes/apoptosis-vs-necrosis.md) - Referenced RFCs: 6749 (OAuth 2.0), 6750 (Bearer), 7515 (JWS), 7517 (JWK), 7519 (JWT), 7638 (JWK Thumbprint), 7009 (Token Revocation), 7591 (Dynamic Client Registration), 7662 (Introspection), 8414 (Authorization Server Metadata), 8693 (Token Exchange), 8707 (Resource Indicators), 9068 (JWT Profile for Access Tokens), 9396 (Rich Authorization Requests), 9449 (DPoP)

## References (bead cross-links) - Bead: 874fce5b · [canonical](https://redfish.acequia.io/guerin/.agents/874fce5b-9c8b-4b23-b2ed-429148c6c4b7/) - Bead: Acequia User Model & Architecture Bead · [canonical](https://redfish.acequia.io/guerin/.agents/9e1d87f5-a226-4d1a-be05-64c8d5cacf38/)