Repository · workstreams
Workstream: W3Wallet
Status: Planned · Component: Maximize developer productivity
An aspirational vision and design-scope document, not a milestone plan. It is intentionally detailed about mechanics — who holds what data; how authority is presented, validated, revoked, renewed, grouped, delegated, delivered, and paid for — because those mechanics are what make or break a capability system. Milestone/sequencing breakdown is deliberately deferred to separate notes.
How to read this document
- North star · Guiding principles — what we are building and the non-negotiable stances.
- Glossary — every invented term in one place; skim it first if the jargon is unfamiliar.
- Current state — what is already built vs. aspirational, so the plan stays honest.
- Threat model — who is trusted, who may be malicious, and what each can/can't do.
- Data ownership — the "who manages what" map.
- Design: … — five themed clusters (capabilities & delegation; validation, revocation & scale; spend & settlement; principals, delivery & recovery; enforcement & trust roots).
- Decision log — forks we have settled, with rationale.
- Deliberate deferrals — what we have chosen to postpone or delegate, and where each lands.
North star
Make authority a first-class, transferable, revocable object — and make authentication, authorization, and quota a solved primitive that every service, page, desktop app, and agent gets for free.
The defining idea is object-capability security: what a principal may do is determined by the capabilities it presents, not by who it is. No central auth server, no passwords on servers, no durable user identity to log in as, no identity-based access-control list in the critical path. A capability is an unforgeable, signed grant that both names an authority and confers the right to exercise it; possessing it (and proving possession) is the permission.
Concretely, all of the following should be true:
- Capabilities are function calls. A capability is invoked like a function and returns a result. Its implementation may embed an API key, a private signing key, or arbitrary code — and the caller sees only the result, never the secret.
- Users grant authority to other users, peer-to-peer. UserA hands UserB a capability with no central authority and no service sign-off, and — in the function-call form — without UserB obtaining the underlying secret. UserA stays in control: sees every use, revokes at will.
- Pages and apps request privileges lazily, like camera/location prompts: start minimal, request more at runtime, the user approves or denies.
- Desktop apps and agents are first-class principals, identified by code signature and/or running instance — not an HTTPS origin they don't have.
- Hosting platforms provision authority to the code they run. A container, a hosted function, a test, or a build rule acts through capabilities the platform injected on the user's behalf — scoped, revocable, and auto-expiring when the workload ends — so it can call an external API under an embedded secret it never sees, instead of an ambient key baked into the host.
- Spend is governed, with per-asset rate limits, buffers, and real settlement, visible to and controllable by the user.
- Securing a service is a few lines, not a sub-project: a layer declares the capability an operation requires and the platform enforces it.
W3Wallet is the security backbone described on its Documentation Repository project page. This workstream sharpens that backbone into the design below.
Guiding principles
- Authority is possession, not identity. Authorization asks "can the caller present and prove a capability?", never "is the caller on an allow-list?". There is deliberately no durable wallet identity / login. Profiles are local, private bundles of capabilities for separating contexts, not a published identity.
- No ambient authority. Nothing is permitted merely for running on the user's machine or arriving from a trusted network. Every privileged action is backed by a presented capability.
- Least authority by construction. You can delegate only authority you hold, and only attenuate it as it passes hands — never amplify.
- Secrets stay with their owner. Keys live in the owner's daemon and are exercised there. Holders of a reference capability get results, never secrets. Token capabilities carry an authorization (not a secret) and are audience-bound so a recipient can't misuse them elsewhere.
- Credentials, not identities — nothing is presented twice. No key, id, address, token, or session artifact is ever presented to more than one counterparty: grants carry per-relationship rendezvous names and keys, tokens are minted per audience, caller peer-ids rotate. A counterparty can correlate only the relationship it is already party to — servers honor credentials rather than tracking identities. (Per-use unlinkability from one's own counterparty — ZK presentation — is deliberately out of v1 scope.)
- Bounded by default. Every append-only structure — audit logs, agreement history, ledger detail, mailboxes, blacklists — ships with a default retention and a size backstop; unbounded growth is a defect. Indefinite archival (via the Event Log) is an explicit choice, never a silent default.
- Best-effort optimization, source-of-truth fallback. Every performance mechanism (grouping, session secrets, blacklists, leases, caches) is soft state. Losing it costs a round-trip, never correctness.
- Revocation and renewal are intrinsic. Capabilities are leased: they expire and must be renewed. Revocation is "stop renewing, and blacklist until the current lease lapses" — bounding the blacklist and making revocation routine.
- Exactly one deliberately-central component. Everything is decentralized except settlement, which v1 anchors on a custodial hub (see Design: spend and settlement). We name this exception explicitly rather than letting centralization creep in unannounced.
Glossary
- Capability — an unforgeable grant to perform an action. Has a public part (type, metadata, public key) and, for the reference form, a private part (the secret).
- Reference capability — a capability whose private part is a reference that calls home to the issuer's daemon at use time; the secret never leaves the issuer. Gives live audit + instant revocation.
- Token capability — a signed, leased, attenuable credential the holder presents; verifiable offline by signature + blacklist; carries no secret. Macaroon-style (see Token capabilities).
- Capability JAR (plugin) — guest code wrapping a secret behind a named, typed function surface, installed into a wallet via
installCapability(jar, factoryClass, secretData)→invoke(functionName, params). The wallet is a generic host that knows nothing about the guest's domain; the JAR runs sandboxed (SJVM — see Design: capabilities and delegation) and declares a typed catalog the wallet uses for blind attenuation and UI. - Authority home — the daemon-or-server that holds a capability's secret and its grant state; the place that says yes/no, logs the use, and can revoke. For a service's resources, the service is the authority home; for a user→user delegation, the issuing user's daemon is.
- Principal — the thing making a call: a browser origin, a native process (by code identity and/or instance identity), an agent, or a hosted workload.
- Hosted-workload principal — a unit of code running on a remote hosting platform (a ContainerNursery app, a LambdaServer function invocation, a kompile test or build-rule run, an agent conversation) rather than on the user's own machine. Identified by platform attestation, not local-OS attestation; capabilities are provisioned into it by the platform, instance-scoped and auto-revoked when the workload is reaped.
- Platform attestation — a hosting platform vouching for the identity of a workload it spawned ("this is invocation N of function F, deployed by user U") — the remote analog of local-OS code/instance attestation.
- Capability provisioning — a hosting platform injecting a (reference or capability-JAR) capability into the code it hosts at spawn/invoke time, so that code can act without holding raw secrets. The inward complement of capability enforcement (gating who may call a service).
- Permissions-management server (PMS) — a service's own optional, best-effort helper that validates, blacklists, renews, groups, and session-caches token-capability traffic. Never a source of truth.
- Lease — a short expiry on a capability or token; renewal extends it, revocation refuses to.
- Blacklist — the bounded set of capability ids revoked before their lease would have lapsed.
- Role / group (durable) vs working-set handle (soft) — two ways to refer to many capabilities at once (see Scaling).
- Asset class / pool / allowance — the spend model: an asset class (denomination + scope) sits in a pool (a balance that accrues) and is drained by rate-limited, buffered allowances that chain like delegation.
- w3coin — the v1 settlement asset: a universal, arbitrarily-divisible asset class used as the unit of settlement. A closed-loop, non-redeemable utility credit — deliberately not money and not a cryptocurrency: it enters circulation only by operator grant and never cashes out (see Design: spend and settlement).
- Custodial hub — the v1 settlement ledger that holds canonical w3coin balances and enforces no-overdraw.
- Spend agreement — a signed bilateral "purchase agreement" governing a standing charge and its cancellation/reclamation.
- Helper daemon — a trusted daemon (ideally the user's own always-on device) that holds a per-capability secret copy so reference caps stay usable when the issuer is offline.
- HostedW3WalletService — a managed option for authority-home hosting: a service that runs a user's own independent wallets on managed hardware (for authorities needing 24/7 reachability or availability isolation), configured from the owner console. Managed hosting of independent user wallets, not a central authority; self-hosting remains preferred.
- Device group — one human's own devices, sharing private state under a device-group key.
- Mailbox / relay — store-and-forward for opaque encrypted+signed envelopes to offline recipients. The mailbox is a built-in function of the wallet daemon — any daemon can serve as one — never a separate service or server.
Current state — built vs. aspirational
The system is built and exercised end-to-end (browser extension + local daemon + url:// P2P + demos).
Already implemented (the foundation):
- Capabilities-as-functions with hidden secrets. The daemon executes a capability's private part and returns only the result. Types: Signing, Proxy (injects
Authorization: Bearer <secret>inside the daemon — the caller never sees the key), Custom, plus a capability-JAR runtime (installCapability(jar, factoryClass, secretData)→ aCapabilityRuntimewith namedinvoke(functionName, params)functions). - Revocation & expiry (intra-wallet). Disable a capability; revoke a permission; expiry at invocation time (
SESSION/1h/1d/1w/PERMANENT;USE/READ/WRITE/READ_WRITE). - Quota primitives. A
SpendKey/SpendLimit/SpendRegistrationtoken-bucket model withRegisterSpendResult = Approved | Tentative(approvedDurationMs) | Declined, exposed to pages — not yet wired to invocation or any consumer. - Browser + server reachability.
window.w3walleton HTTPS pages; aurl://P2P daemon service (url://w3wallet.daemon.<peerId>/…);X-W3Wallet-Daemon-Urlheader injection; multiple local profiles.
Aspirational / not yet built (the substance of this workstream): cross-user delegation (today's sharing is intra-wallet, cross-domain only — granted_to is a domain string, never another wallet); token capabilities; the PMS; invocation audit; general lazy permission prompts (the existing balloon fires only on re-enabling a disabled cap); non-browser principals (and platform-provisioned, instance-scoped capabilities for hosted workloads — designed for the agent case in AiCliHostSupervisor, not yet generalized or built); offline delivery; multi-device sync & recovery; spend wired into enforcement and real settlement; and a turnkey enforcement gate (every consumer hand-rolls ~15–30 lines, and at least one production service advertises capability verification its handler does not perform — a drift to correct).
The daemon today intentionally rotates its peer-id on every startup and has no stable identity key, mailbox, cross-wallet grant, group/session token, server-side registry, or recovery seed. The design adds the missing machinery while honoring "no durable identity."
Implementation status
Snapshot: 2026-07-04. The design below is now substantially built and merged — the object-capability core, the PMS, spend, settlement, the turnkey enforcement gate, and the daemon-embedded mailbox all exist and are exercised end-to-end by a hermetic full-stack integration suite. Most of the "aspirational / not yet built" list in the Current state — built vs. aspirational section above has since landed; this section records what shipped and links the work.
Object-capability core — shipped & merged:
- Token capabilities — macaroon-style Ed25519 mint / attenuate / verify / revoke / renew with channel-bound session keys: W3WalletCapabilityTokens #1, #2.
- Reference-capability delegation across wallets + invocation audit: W3WalletDaemon #117.
- Permissions-Management Server (PMS) — service-run validation oracle, bounded blacklist, lease renewal/re-sign, session secrets, grouping — as
url://w3wallet-pms/: Api #1 · #2, Embedded #1 · #2, ServiceServer #1 · #2. - Spend — asset pools, rate-limited allowances, standing registrations, leases: W3WalletAssetPools #1.
- Settlement — the w3coin custodial hub (v1 placeholder ledger, no-overdraw) as
url://w3coin-hub/: Api #1, Embedded #1, ServiceServer #1. - Turnkey enforcement gate — one-line
Gate.protect(requirement) { op }fail-closed pipeline (offline verify, PMS oracle, spend enforcement, audit effects): W3WalletGate #1; plus a hermetic full-stack integration suite composing every component (tokens → PMS → gate → spend → settlement → delegation): #2. - Store-and-forward mailbox, embedded in the daemon (never a standalone service): W3WalletMailboxApi #1, W3WalletMailboxEmbedded #1, embedded via W3WalletDaemon #116; the standalone-server repo retired in W3WalletMailboxServiceServer #2.
- url:// transport origin hardening — reject self-asserted trusted (extension) origins on url:// / relay: W3WalletDaemon #114.
Consumer adoption (the gate is genuinely drop-in):
- KompileRemoteBuildServiceServer (formerly KompileBuildCiServiceServer) now gates its previously-unauthenticated
ingestNowRPC through the turnkey gate (server-minted single-use challenge nonce as channel binding; enforcement opt-in): KompileRemoteBuildServiceServer #11.
Browser extension:
- MV3 service-worker stale daemon-URL header fix: W3WalletExtension #52.
Open / in flight:
- Security — url:// capability-op origin bypass (issue #115): W3WalletDaemon #118 closes the capability owner-shortcut and the self-asserted-origin permission fallback over url:// — remote cross-domain access must go through the credential-based grant rendezvous; CI green, awaiting merge. A related write-attribution surface is tracked in issue #119.
- Browser-behind-NAT e2e — W3WalletTests #25. The shared NetLab infrastructure OOM that gated it is fixed (UrlResolver #669, merged); the remaining failures are in the browser-nat demo flow itself.
Remaining plan work (tracked as issues):
- The daemon adopts the AssetPools engine + w3coin settlement, retiring the external
BillingManagerEmbeddableCoreand unifying wallets with spend keys: W3WalletDaemon #91, #92. USE-permission tier hardening (aREADgrant must not satisfy aUSEcheck): see W3WalletDaemon #97 (partially superseded by later origin fixes).
Plan-document evolution: vision + detailed mechanics PlanRepository #14 · #15; open-question resolutions #69 · #84; placeholder-ledger settlement #17; mailbox-embedded #87; browser-nat e2e state #50.
Threat model and trust assumptions
Stating who is trusted keeps the design honest about what each mechanism defends.
Trusted (honest within stated bounds):
- The user's own devices in their device group. The user trusts their own machines; a compromised device is handled by eviction/force-revoke, not by distrusting devices in general.
- A service's own PMS. It is the service's own component (see Decision log — PMS is service-run only), so it learns nothing the service didn't already know.
- The custodial hub, for settlement integrity only. It is the one central trust anchor (v1). It is trusted to keep balances correct — and in the target design is constrained: it cannot authorize spend (that needs the user's keys), cannot read capability secrets or payloads (it sees only w3coin amounts and counterparties), and its balances are meant to be reconcilable/auditable. It is honest-but-must-be-audited. (The v1 placeholder ledger does not yet enforce these constraints — its security/privacy are explicitly deferred; see Settlement → v1 bootstrap.)
Untrusted / potentially adversarial:
- Counterparties — a service you call, or another user you delegate to/from. May try to over-charge, replay a presented capability elsewhere, or exfiltrate. Defended by: secrets-stay-home (reference caps), audience caveats + session channel binding (anti-replay), leases/caps/allowances (bounded blast radius), and spend agreements.
- Relays and mailboxes — untrusted for confidentiality and integrity (envelopes are encrypted + signed) but relied on for availability (they can drop/censor — mitigated by redundancy).
- A third-party helper holding a full secret copy — trusted only as far as the user opts in; mitigated by per-capability scoping, report-back audit, and revoke-by-rotation.
- The network — untrusted; sensitive channels are authenticated and encrypted.
Explicitly out of scope: a fully-compromised, unlocked user device (it can do anything the user can until evicted); and the external funding boundary, which v1 reduces to operator grants (w3coin is a closed-loop credit with no cash-out — see Design: spend and settlement) and which stays out of the identity-free capability layer.
Data ownership — who manages what
No row requires a durable public identity; the only cross-device shared secret is private to one human's device group.
| Data | Owner / location | Notes |
|---|---|---|
| Owned-capability secrets (API/signing keys, capability-JAR secret data) | The issuer's daemon, encrypted at rest | Never leaves the owner; exercised in place. |
| Held capabilities (references + grant credentials; token caps received) | The holder's daemon (source of truth) | Synced across the holder's own devices. |
| Issued-grant ledger (who I granted, lease, attenuation, status) | The issuer's daemon | Basis for the issuer's audit + reference-cap revocation. |
| Invocation audit log | The authority that executes (issuer's daemon for reference caps) | 90-day rolling retention + size backstop; stream to the Event Log for indefinite archive. |
| Authority signing key (mints/renews a service's token caps) | The granting authority | Its public key is a trust root for verifiers. |
| Revocation blacklist | The service's PMS, published from the authority | Bounded: entries drop once the lease would have lapsed. |
| Role/group definitions (durable) | The granting authority / its PMS | One membership cap expands to many; revoke in one op. |
| Working-set handles + session secrets (soft) | Both user daemon and verifying server, cached | Pure optimization; re-established on a miss. |
| Asset pools & allowances (balances, accrual, rate limits, registrations) | User's daemon holds the authoritative balance; leased across devices | The metering point; user-visible; backstopped by the hub's no-overdraw guarantee. |
| Canonical w3coin balances | The custodial hub | v1 settlement source of truth; enforces no-overdraw. |
| Resource-server per-user state | Minimal / none | Only a soft session cache; the user's daemon is the source of truth. |
| Device-group sync key | Shared privately across one human's devices | Enables sync + recovery; not a public identity. |
| Encrypted wallet backup | The user's own helper/cloud daemon, E2E-encrypted | Last-device recovery; opened only by the user's recovery key. |
| Mailbox envelopes | A mailbox — another wallet daemon serving in its built-in mailbox role — holds opaque encrypted+signed blobs | The mailbox daemon can't read or forge; only stores-and-forwards. Envelopes expire (30-day TTL). |
| Hub recovery code | User-held, offline (optionally Shamir-split) | Rotates the hub credential only; confined to settlement. |
Design: capabilities and delegation
Capabilities as functions. A capability has a public part and, for the reference form, a private part held only by the issuer's daemon. Invoking is invoke(capabilityId, functionName, params) → result; the daemon runs the private part and returns the result. A Proxy capability makes "use my API key against service S" a callable function the holder uses without seeing the key; a capability-JAR makes arbitrary secret-wrapping code a named, typed function surface.
Capability JARs — the wallet as a sandboxed plugin host. The capability-JAR form makes the wallet a generic runtime for guest code it knows nothing about: installCapability(jar, factoryClass, secretData) loads a JAR and exposes its named functions via invoke(functionName, params), with the secret stored as opaque bytes the wallet never interprets — it does not know a secretData blob is a GitHub PAT, a Stripe key, or a signing key. All domain knowledge — the typed functions, the outbound calls, the secret's meaning — lives in the JAR; nothing about any specific service is compiled into the wallet (capability JAR : wallet :: app : browser :: function : LambdaServer).
Because a capability JAR runs in-process with access to its own secret, loading guest code beside every other capability's secret is the central risk, so the runtime must sandbox each JAR — concretely via SandboxJVM (SJVM), whose guest bytecode "cannot access the host filesystem, network, or any other host resources unless you explicitly provide them." A sandboxed JAR sees only its own secretData (never a sibling capability's secret, even co-resident in the same JVM) and reaches only its declared egress (e.g. github.com), so a malicious or buggy plugin can neither exfiltrate another capability's key nor attack the user's machine. Egress and resource limits are declared in the plugin's signed manifest (beside its catalog: allowed hosts — exact names or narrow wildcards, HTTPS-only by default — plus memory, wall-time, and response-size caps) and enforced structurally: the guest gets no ambient network at all, only an injected HTTP client restricted to the declared allowlist (which performs its own name resolution, closing DNS-rebinding). Install approval is consent to the manifest, not to the code — a plugin is never trusted beyond it, there is no "trusted plugin" tier that relaxes the sandbox, and an upgrade that changes the manifest re-prompts with a diff. This is the same sandboxing lineage behind UrlResolver's sandboxed execution and RemoteJVM's in-process backend.
Blind attenuation and blind UI. A domain-blind wallet must still enforce "only repo X, only createIssue" and render a permission list. It does so with no domain knowledge by reading the JAR's declared catalog (the existing CapabilityMetadata/CapabilityFunction schema, evolved in place): parameters use a closed set of wire types, and each parameter declares its applicable caveat dimensions from a closed constraint vocabulary — pin-exact, one-of, range, prefix — enforced by a generic matcher, so per-hop attenuation can only tighten declared dimensions and attenuate-only falls out structurally. A function may additionally declare a spend dimension (asset class + cost per call or per unit) so allowances attach and meter generically. The signed, versioned catalog travels in the capability's public part — a grantee's wallet renders functions and permissions without ever seeing the JAR, and the owner/coordinator console lists them the same way; there is no central catalog registry, and the wire format is the implementation repo's decision. (A closed vocabulary is chosen over JSON-Schema-style expressiveness deliberately: blind attenuation needs a decidable "is this invocation within this caveat" check, and a constraint matcher is trivially decidable where subschema relations are not.) The platform stays domain-agnostic; the plugin supplies the schema.
Two representations. A single conceptual capability is carried in one of two concrete forms, chosen by the issuer; they interoperate and a chain can mix them.
| Reference capability | Token capability | |
|---|---|---|
| What the holder gets | A handle that calls home to the issuer's daemon | A signed, leased grant the holder presents |
| Secret location | Stays on the issuer's daemon; never leaves | No secret — an authorization, not a key |
| Use-time dependency | Issuer's daemon (or a helper) must be reachable | Verifiable offline by signature + blacklist check |
| Audit | Issuer sees every invocation | Issuer sees nothing unless the verifier reports |
| Revocation | Instant: reject the next call | Stop renewing + blacklist until lease lapses |
| Best for | "Function call wrapping my API key; gate every use" | "I hold access to 10,000 things; prove it efficiently, use it offline" |
| Built on | Today's Proxy capability, pointed at a url:// wallet |
New: signed, attenuable, leased credential |
Delegation by reference — the capability chain. When UserA grants UserB a function-call capability, A sends no secret. A mints a new capability B holds, whose private part is a reference to (1) the grant's own rendezvous address at A's daemon (a per-grant url:// name + keypair — see Self-hosted authority home), (2) the upstream capability A retains, and (3) a grant credential B presents. B's invocation proxies up to A's daemon, which authenticates the credential, checks it isn't revoked/expired, records the use, runs the real private part, and returns only the result. So A keeps the secret, sees every use, and revokes unilaterally. It is the same shape as today's Proxy capability — pointed at the grant's rendezvous name with an injected grant credential instead of at an HTTPS API with an injected key. The upstream part may itself be another reference, so delegation composes (C → B → A); each hop independently audits and may only attenuate (restrict functions, pin parameters, cap an allowance, shorten the lease).
Token capabilities — signed, leased, presentable. For "prove I hold many things efficiently, possibly offline," a capability is a macaroon-style signed credential: a base grant plus caveats that attenuate it, chained so a holder can add caveats but never remove them (this is why token format is macaroon-style — holder-side offline attenuation is structural). It carries the granting authority's reference, the authorized scope, an audience caveat (which verifier may receive it), a lease, and the signature chain. A verifier checks it offline: validate the chain against a trust root, check caveats (audience, time, first-party conditions), and confirm the id isn't blacklisted. The price vs. reference caps: the issuer sees no usage, and revocation is "blacklist + lease expiry," not instant. Presenting a token is safe (it's an authorization, not a secret) because four anti-replay/confused-deputy invariants hold by construction: audience caveats are mandatory — no bearer token exists in the system, and a verifier rejects tokens not addressed to it; tokens are holder-bound — each delegation hop binds to the next holder's key, so presentation is a proof of possession the verifier cannot re-present elsewhere; session keys are channel-bound — captured fast-path traffic is useless on any other connection; and deputies re-present — a service acting for a caller presents the caller's attenuated capability, never its own authority ("authority flows with the request"). The formal model and protocol spec of these invariants live with the implementation.
Design: validation, revocation, and scale
The permissions-management server (PMS). A service may run a PMS — its own, optional, best-effort assistant (never a source of truth). It is a validation oracle (verify chains/caveats so each resource server needn't reimplement crypto), the revocation blacklist holder, a renewer (it holds a scoped renewal-signing delegation from the authority and re-signs leases before they lapse — so "extend access" and "cut off access" are the same lever pulled opposite ways), the grouping engine (see below), and the session-secret establisher: after the initial PKI-bearing presentation it derives a symmetric session key with the user's daemon, so subsequent requests use a cheap MAC + a group handle instead of re-verifying signatures, and the session key channel-binds the presentation against replay. If the PMS is down or has forgotten a session, the client re-presents the underlying signed capabilities and redoes PKI — slower, still correct.
Revocation & renewal. Three paths: reference caps revoke instantly (reject the grant credential; cascades downstream automatically). Token caps use lease + blacklist (stop renewing; blacklist the id only until its short lease would have lapsed, keeping the list bounded no matter how many were ever issued; lease length trades revocation latency against renewal traffic). Bulk revocation bumps a monotonic epoch on a resource/group/relationship, invalidating all caps minted under the old epoch in one operation. Token caps carry parent lineage, so blacklisting a parent invalidates descendants. (Time model: durations are checked against the enforcing party's own NTP-synced wall clock with a bounded skew tolerance, default ±30 s — safe because in every mechanism here the enforcing party is also the one bearing the risk of being wrong, so clock manipulation only self-harms and no trusted global time source is needed; ordering — revocation-before-use, epoch propagation, audit order — uses HierarchicalClock timestamps, which cannot measure elapsed time but prove causality; every implementation takes an injectable Clock.)
Scaling to many capabilities (the 10,000-lambda case). Two complementary mechanisms: durable roles — the authority defines a named role ("project-X functions") that expands server-side to many resources; the user holds one membership token; grant/revoke is one op — and the soft working-set handle — for arbitrary sets, the daemon presents the set once (ideally a single signed bundle: one signature over the set, or a Merkle root with per-item proofs) and the PMS issues an opaque group handle + session secret, both sides caching handle → members. End to end: connect (present a role membership or a signed bundle) → PMS verifies once, expands roles, checks the blacklist → returns a group handle + session secret → subsequent requests carry the handle + a MAC (no PKI, no re-verification; the WUI lists functions by expanding the cached group) → revoke by blacklisting a member or bumping the epoch (handle re-established on the next miss, dropping the revoked member; staleness bounded by the handle TTL) → renew by re-signing before leases lapse.
Design: spend and settlement
Asset pools and allowances. Quota is one model: asset pools drained through rate-limited allowances, forming a flow network. There is no "real money vs. credits" distinction — both are asset classes, each with a denomination (usd, btc, lambdaserver:credits, sfs:gb-days, …) and a scope (universal, spendable anywhere, or bound, spendable only at one site). A third-party API key's rate limit shared across several grantee wallets is exactly this shape — the key's budget is a pool (e.g. github:rest-req) and each grantee holds a rate-limited allowance against it; rate-bounded sub-allowances need no coordination at all, while a single bursty budget uses the leased-balance coordinator below. This pool/allowance model is also the ecosystem-wide rate-limiting pattern — every service is encouraged to enforce rate limits through it rather than a bespoke throttle; the service-facing guidance lives in architecture/RATE_LIMITS.md.
- Pools hold a balance of one asset class and accrue on a schedule or in bursts (a top-up, a grant). This subsumes the existing token bucket:
SpendLimit{ accrualRate, maxCarryover, currentBalance }is a pool whose accrual is a steady rate andmaxCarryoverits buffer cap. A pool or allowance may carry multiple buckets — several(accrualRate, cap)pairs enforced simultaneously (10 req/sburst-smoothing and10,000 req/daybudget) — and a draw must satisfy every bucket; all buckets refill continuously, so an exhausted caller's progress slows to the accrual rate rather than waiting on a reset cliff. Per-hop attenuation may add buckets or tighten them, never loosen. - Allowances are rate-limited, buffered draws from a pool or another allowance ("≤ $5/hr, buffer ≤ $15"). An allowance is the unit you hand a service — an attenuated spend capability whose authority home is your daemon; revoking it (or cancelling a registration) is your stop lever; it shows in your wallet with rate, buffer, source, and live draw.
- Allowance chains are offline-safe by construction. Because an allowance can draw from another allowance, spend chains like delegation (A → B → C). If each rate never exceeds its source's, the chain runs indefinitely offline — the cap is structural, carried in the grant. Three backing regimes: a rate-bounded sub-allowance (rate ≤ source) is self-enforcing offline (the filesystem's "$0.10/day forever"); a pool drawdown is offline-safe up to the balance; a buffered draw absorbs bursts/offline gaps while the long-run rate stays the hard cap.
- Standing registrations & over-commitment. A service reserving an ongoing rate registers an allowance;
RegisterSpendResult = Approved | Tentative(approvedDurationMs) | Declinedalready approves only for the duration the buffer sustains when total committed rate exceeds accrual, after which it renews or lapses. Every registration is visible (getSpendKeyStatus/listRegistrations) with who/rate/since/sustainable-for. - Oversubscription & fair distribution. Granted sub-allowance rates may deliberately sum past what their parent sustains — statistical multiplexing, so mostly-idle consumers can burst higher than a strict partition would allow. While the parent has headroom, draws are first-come up to each allowance's own limits; when the parent is at capacity, its accrual is divided among actively-drawing consumers by a 50/50 hybrid rule — half by weighted max-min fairness (proportional to each active consumer's granted rate, unused share redistributed to those still contending) and half split equally among active consumers — so heavy consumers keep some priority while every light consumer keeps a guaranteed floor; no one starves, and idle consumers cost nothing. Enforcement needs no new component: it is a lease-sizing policy at the pool's authoritative holder (see Leased-balance coordination) — under contention, granted slices shrink to each lessee's hybrid fair share. Corollary, stated plainly: an oversubscribed allowance is not fully offline-capable under contention — lease renewal through the coordinator is where fairness is applied.
- Exhaustion parks, never cliffs. When a draw cannot be funded, the enforcement gate parks the request until accrual funds it rather than rejecting: the caller keeps a parked draw alive via a connection-scoped lease (the Observables GC-lease pattern — renewed while the consumer stays connected and interested), and a draw whose lease lapses is collected and its claim released. Refill funds parked draws FIFO within a consumer and by the hybrid fair share across consumers. This is also the stampede defense: no reject-and-retry loops, no synchronized reset instant — under overload every consumer's progress degrades smoothly toward its fair share of the accrual rate.
Default accounting for delegated paid capabilities: the secret owner's pool drains, unless the issuer attaches an explicit sub-allowance — just another rate-bounded edge.
Settlement: w3coin and the custodial hub. W3Wallet performs real settlement — value moves, it isn't only metered. The v1 settlement asset is w3coin (a universal, arbitrarily-divisible ledger unit; the existing …Cents integers generalize to fractional w3coin). A service is paid by w3coin moving from payer to payee on a ledger run by a centralized custodial hub — a W3Wallet-operated ledger that holds canonical balances and is the settlement source of truth that enforces the no-overdraw guarantee (the hard backstop the lease coordinator relies on). This is the one deliberately-central component, and we accept it for v1 with eyes open and three honest mitigations:
- It only settles. Metering, allowances, and authorization stay client-side and keep working during a hub outage (within outstanding leases). The hub never authorizes spend (that needs the user's keys) and never sees capability secrets or payloads — only w3coin amounts and counterparties. Even that visibility is narrowed: users may hold multiple hub accounts (per-context, like profiles) to partition their settlement graph, and settlement is batched/netted rather than per-invocation, shrinking both the hub's flow-level view and its write load. (Cryptographically-private settlement — blinded, e-cash-style — remains a possible future hub upgrade behind the same adapter abstraction.)
- The boundary is thin. Most spend nets out inside the ledger; the external funding boundary is crossed only at top-up and cash-out, via a pluggable funding adapter per asset class. The funding-adapter abstraction is what lets a later version swap the hub for federated hubs or a decentralized ledger without touching the spend graph.
- The funding boundary is trivial by choice: v1 w3coin is a closed-loop credit. w3coin is a non-redeemable utility credit — not money, not a cryptocurrency. The only v1 funding adapter is an operator grant/faucet (the hub operator mints and allocates budgets), and there is no cash-out — so grants are irrevocable once minted (no clawed-back-top-up edge case exists), nothing redeemable ever crosses the boundary, and the identity-free capability layer stays free of money-transmission/KYC pressure. Real-money purchase or crypto backing are future adapters, and any regulatory machinery attaches at those adapters, never inside the capability layer. The user's pseudonymous hub credential lives in their encrypted backup, confined to settlement and never pushed into the capability layer.
v1 bootstrap — a placeholder ledger. The custodial hub above is the target shape; the first implementation is deliberately a minimal placeholder settlement service — a simple server + database that tracks pools, balances, and transactions and enforces no-overdraw, nothing more. It is explicitly not yet secure, private, or production-grade; its only job is to give the rest of the model (allowances, leases, spend agreements, metering) a real settlement backend to build and test against, so we can make forward progress. Because the spend graph and funding-adapter abstraction sit above it, this placeholder is expected to be hardened or swapped wholesale (for a real custodial hub, federated hubs, or a decentralized ledger) without touching the layers above. Treat its security/privacy gaps as known and deferred, not as design positions.
Because balances live on the hub, total device loss does not lose funds — recovery restores the hub credential (see recovery).
Leased-balance coordination. A pool has one authoritative holder (your always-on helper, or one you designate) owning the canonical balance. Other devices/services never draw directly — they take a lease: a short-lived signed grant of a bounded slice (amount and/or rate cap) for a TTL ("phone may spend ≤ 5 w3coin at ≤ 2 w3coin/hr until T+5min"). A leased slice is reserved, so lessees hold disjoint slices — double-spend is impossible by construction and a lessee spends locally, even offline, bounded by its lease. A lessee may either hold a standing lease (a reserved slice it spends locally/offline, token-style) or re-request per draw from the holder (reference-style: live audit + instant revoke, at the cost of a round-trip and no offline capability) — the same reference/token tradeoff, the issuer's choice; a rate-bounded sub-allowance (rate ≤ source) needs no lease at all and is held directly with zero coordination. On expiry, unspent reservation returns and spend reconciles. A reserved slice carried as a token is paid to the seller in full at mint — cash-like, so it is spendable offline, double-spend-safe, and leaves the seller zero counterparty risk — but it is metered only after the fact: the seller's signed usage report (to the issuer's url:// endpoint, or its mailbox when the issuer is offline) refunds the unspent remainder to the buyer. A seller that never reports simply keeps the full slice, so buyers size minted slices to expected usage, and spend agreements may make refund-on-report a machine-checkable term (deferred audit and refunds are the price of offline use). If the holder is unreachable, lessees spend within outstanding leases but can't renew; on split-brain the hub's no-overdraw rule bounds damage to a temporary over-promise, never real overspend. Exposed to the user: a live per-pool view (balance, free-vs-reserved, who holds which leases); designation of the holder and a fallback order; per-device lease caps; a per-device offline budget (the bounded risk accepted for disconnected spend); and force-revoke of a lost device's leases (no new spend; already-bounded spend still settles). Defaults (holder-overridable, user-visible): lease TTL 5 minutes, renewed at half-life; slice size ≈ 2 × (the device's allowance rate × TTL), capped at 10 % of the pool — and, while the pool is contended, re-sized down to each lessee's hybrid fair share (see Oversubscription & fair distribution above); offline budgets are explicit and off by default — enabling one is precisely accepting bounded disconnected-spend risk. Over-promise reconciliation: under the target hub's RPO = 0 a failover cannot lose reservations, so the only over-promise source is offline spend past an expired lease (a placeholder-era failover reconciles through the same path); the pool then carries the deficit — its balance goes negative, accrual and top-ups pay the deficit down before any new spend, no new leases are granted while in deficit, and the offending device's offline budget requires user re-approval. Sellers who accept expired-lease spend bear that non-payment risk themselves; an honest buyer's wallet pays it down automatically.
Hub resilience and outage behavior. Because the hub only settles and is never in the authorization path, an outage degrades narrowly: capability invocation, metering, allowance enforcement, and spend within outstanding leases all continue (the last offline, against reserved disjoint slices), while only new lease grants / renewal against the canonical balance, final settlement, and the funding boundary pause and resume on recovery. Split-brain is bounded by the no-overdraw rule to a temporary over-promise, never real overspend. Failover is treated as standard high-availability of a strongly-consistent ledger: the no-overdraw invariant forces linearizable balance updates, so the target hub is a consensus-replicated state machine — a single node failing is not an outage — and eventually-consistent multi-master is ruled out for the canonical balance. The target guarantees are pinned: RPO = 0 for committed settlements (quorum commit before acknowledgment), automatic failover with RTO ≤ ~30 s, on a 3-node quorum tolerating one machine failure; the choice of consensus implementation is delegated to the hub repository's design docs. Ledger storage is bounded by checkpointing: canonical balances live forever, but per-transaction detail is retained (default 1 year) beneath periodic signed balance checkpoints, preserving auditability across the retained window plus the checkpoint chain. The v1 placeholder stays single-node (resilience deferred with the rest of the placeholder); federated hubs / a decentralized ledger via the funding-adapter abstraction is the long-term answer to the single-point-of-failure.
Spend agreements — cancellation ↔ reclamation. A standing registration is a signed bilateral agreement ("purchase agreement") both buyer and seller hold, with machine-checkable terms: resource, rate and asset, start, optional max duration, a grace period on non-payment, reclamation terms ("delete stored data after a 7-day grace"), and cancellation terms (cancel anytime; seller releases within a window; remainder pro-rated and settled). Lifecycle:
- Active — the allowance funds the rate; the seller provides the resource.
- Buyer-cancelled — user cancels → allowance stops → a cancellation notice (referencing the agreement) reaches the seller (over
url://, or queued in its offline mailbox) → seller releases and confirms → final pro-rated settlement → Closed. Until confirmation it shows as closing, so pending reclamation is visible. Closing is bounded: the terms include a seller-confirmation window (default: the grace period, 7 days), and on lapse the buyer's daemon unilaterally finalizes — settlement pro-rated as of the cancellation timestamp, state Closed (unconfirmed), with the signed cancellation notice and delivery receipt retained as the audit record. (Nothing of the buyer's is at risk meanwhile — funding already stopped, and reservations lapse on their own — so the deadline is bookkeeping hygiene, not safety.) - Delinquent — the pool can't fund the rate → grace; both notified → funded in time ⇒ Active, else seller reclaims per terms → Closed (defaulted).
- Seller-terminated — seller stops providing → buyer stops paying → settle → Closed.
Machine-checkable terms let both daemons enforce the agreement automatically; a fuller programmable-contract form is a possible extension, not a requirement. Closed agreements are retained for 1 year by default, then pruned to a compact signed final-settlement summary. Each asset class is its own pool and flow — the wallet does no cross-asset FX.
Shared infrastructure pays its way through this same spend system. The PMS is the service's own component and therefore its own cost; self-hosted helpers and mailboxes are their owner's cost — self-hosting is the free tier of everything. Public relays, mailbox-serving daemons, and HostedW3WalletService meter via ordinary standing spend agreements (envelope-days stored, GB forwarded) — no new billing machinery, and the best possible dogfood of the model. The v1 hub is operator-run and free (fees in operator-granted closed-loop credit would be circular); a per-settlement fee, expressed as an ordinary allowance, is the reserved hook for when federated hubs need compensating. Metering enforcement on public infra is a per-operator switch, off in v1 — which also dissolves the bootstrap circularity of needing a relay to pay for a relay.
Design: principals, delivery, and recovery
Principals. Authorization names the caller. Browser origin (exists): pages scoped to their HTTPS origin, cross-origin use requires approval. Native process (new): desktop apps/CLIs/agents register with the local daemon and are identified by OS-attested code identity (signed-binary identity → "all instances of this app") and/or instance identity (pid + verified lineage → "this one running instance," auto-revoked on exit); the daemon shows a native approval prompt and a live, revocable view of running principals. Attestation follows a per-OS ladder — kernel peer credentials for instance identity everywhere; the OS signing primitive for code identity where one exists (macOS code-signing identity, Windows Authenticode publisher) and the binary hash on Linux — and every registration is tagged with its attestation strength, surfaced in the approval prompt; a grant may require a minimum strength. Attestation buys granularity and auditability (your IDE vs. a random script), not defense against a hostile user account — a fully-compromised unlocked device is already out of scope. Mechanics are the daemon repo's concern. Agent (also serves agentic swarms): a native principal that can additionally hold delegated capabilities and bounded allowances, giving a swarm scoped, auditable, revocable authority and spend instead of a shared ambient key. Hosted workload (new): a unit of code running on a remote hosting platform — a ContainerNursery app, a LambdaServer function invocation, a kompile test or build-rule run, an agent conversation — identified not by local-OS attestation but by platform attestation (the platform vouches for the workload it spawned). The platform provisions capabilities into it on the user's behalf, instance-scoped and auto-revoked on reap (see Capability provisioning below); an agent is the swarm-flavored hosted workload that additionally carries its own delegated set and budget.
Capability provisioning — platforms granting authority to the code they host. Turnkey enforcement (see enforcement and trust roots) gates who may call a service; this is the other direction — a hosting platform provisions capabilities into the code it runs, so a hosted workload acts through scoped, revocable capabilities instead of an ambient secret baked into the host. The motivating shape is the capabilities-as-functions primitive turned inward: a unit of hosted code must execute something with a secret embedded — e.g. call an external API under an API key — without that secret ever being revealed to the code itself. Three platforms drive the need:
- ContainerNursery provisions capabilities into the apps it hosts — a deployed app calls an external API through an injected Proxy/reference capability rather than reading a key from its environment.
- LambdaServer provisions capabilities into the functions it invokes — a registered function is granted a capability at invocation time and exercises it without ever holding the raw secret.
- Kotlin Build (kompile) provisions capabilities into tests (and possibly build rules) — a test that must reach an external API or a credential-bearing service does so through a provisioned capability, keeping secrets out of test source and out of the test process.
The grant is a delegation chain, user → platform → workload: the user attaches a capability to a deployment / function registration / test config; the platform may only attenuate it (restrict functions, pin parameters, cap an allowance, shorten the lease) and provisions it to the spawned workload; the workload invokes it and receives only results. Two provisioning forms, the same trade-off as the two representations above:
- Reference form (preferred on untrusted / multi-tenant hosts). The secret never enters the host at all: the workload holds a handle that calls home to the owner's daemon, which runs the private part and returns the result. Even a fully-compromised container, function, or test cannot exfiltrate a secret it never received, and the owner sees every use and revokes unilaterally. This is the strongest answer to "without revealing the secret to the caller."
- Embedded capability-JAR form (lower latency, no call-home dependency). The secret-wrapping code runs in a capability-JAR runtime inside the workload's host; the workload calls
invoke(...)and never sees the raw secret, but a fully-compromised host could in principle extract it from memory. Suitable when the host is trusted or call-home latency/availability is unacceptable — but the reference form is preferred wherever the hosted code is untrusted.
The workload is a hosted-workload principal: instance-scoped and lifecycle-bound — the provisioned capability's lease is tied to the workload's lifetime (container sleep/teardown, invocation completion, test/build-rule finish) and auto-revokes when the workload is reaped, the remote analog of native instance-identity auto-revoke-on-exit. The binding is a fresh per-instance keypair injected at spawn: the grant credential is audience-bound to that key, every call-home proves possession (a co-tenant that captures the credential holds nothing), and the platform signs the instance attestation ("instance I of deployment D") with the platform key the user trusted when attaching the capability. Lifecycle is enforced by renewal, not memory: leases are short and the platform renews them only while the instance lives, so a reaped workload's capability dies within one TTL even if explicit revocation is missed. On multi-tenant hosts provisioning is reference-only by default; the embedded capability-JAR form requires the owner's explicit opt-in that the host is trusted. (Attestation is only as strong as the platform's own integrity — the platform is inherently in the TCB for the workloads it hosts, which is exactly why the reference form is the default posture.) AiCliHostSupervisor is the worked example already designed in depth: a supervisor↔guest handshake negotiates the workload's W3Wallet identity, MyLittleAgi delegates the granted capabilities to it instance-scoped, and they auto-expire on reap — exactly this pattern, specialized to agent conversations. This section names the general shape that one consumer's design is an instance of.
Lazy / dynamic permission requests. Any principal may request authority it doesn't hold at runtime; the daemon surfaces an approve/deny prompt (browser balloon / native dialog) with the requested scope, duration, and any spend rate — like a browser permission prompt. Principals declare a minimal baseline and escalate on demand.
Offline delivery — sharing with a user who isn't online. Without a durable identity, an offline recipient is reached via store-and-forward mailboxes: the recipient publishes (out of band, like exchanging a contact) an ephemeral receiving key plus 2–3 mailbox addresses — their own always-on helper as primary plus independent fallbacks. The mailbox is a built-in function of the wallet daemon, not a separate service or server: any daemon can serve as one, so a mailbox address is just another wallet (your own helper, a friend's wallet, a hosted wallet) agreeing to hold envelopes. The grantor mints the capability, binds it to that key (first use requires proof of possession, so a leaked or mis-delivered envelope is worthless), encrypts the envelope to it, signs it, and deposits it to every listed mailbox, retrying until the recipient's signed acknowledgment arrives; the recipient polls its mailboxes and dedupes by envelope id, and envelopes expire after a 30-day TTL. Binding + end-to-end encryption is unconditional — there is no transport-only mode, since the threat model already declares relays untrusted for confidentiality and integrity — and redundancy lives entirely at the edges: mailboxes stay dumb, with no inter-mailbox replication protocol. Use-time availability is separate: a delivered token cap is usable offline thereafter; a reference cap needs the issuer (or a helper) reachable at invoke time.
Multi-device sync. A wallet spans the user's devices; shared state replicates across a device group under a device-group sync key set by pairing. Held capabilities, owned secrets/signing keys, the issued-grant ledger, audit, and pool/lease state sync over an E2E-encrypted channel. An always-on device serves as helper (authority home for the user's reference caps while the laptop sleeps; it holds a full, per-capability secret copy — the lowest-trust place for one, since it's still the user's own device). Losing one device → evict it by rotating the device-group key. Blast radius depends on the form authority took: reference caps others hold need no re-issue (the indirection means holders never had the secret — rotate the device-group key, plus rotate any raw secret that was resident on the device at its source, and holders' references transparently follow); token caps others hold are re-minted via a single epoch bump (a self-contained offline token can't be transparently re-pointed). Keeping authority in reference form therefore bounds device compromise to a key rotation with zero re-issue.
Self-hosted authority home — run your own always-on helper. The always-on helper is also how a user hosts capability access for others: it is the authority home that holds a key (as a sandboxed capability JAR) and answers the reference invocations of every wallet the owner granted use to — metering, auditing, and revoking centrally, on a daemon the user controls (a home box, a small VPS, an always-on desktop). Where you can run it, "run your own service" collapses into "run your own helper" — the recommended default, with no dependence on anyone else's infrastructure. The trade is structural, not a missing feature — live audit + instant revocation + live aggregate metering require an always-on authority home, because reference caps must reach it at use time; a fully-offline posture forces token caps, usable offline within their lease but giving up live audit/instant-revoke and reconciling metering only after the fact (the reason offline payments can double-spend). For others to reference into a self-hosted helper, the stable thing is the grant, never the host: each grant embeds its own random rendezvous name (url://w3wallet.grant.<token>/) and a per-grant keypair; the helper — whose peer-id keeps rotating every startup, exactly as today — announces each active grant's rendezvous name on the url:// mesh and proves possession of that grant's key when answering (NAT reachability rides UrlResolver's relay fallback; a per-grant key rotates by signed chain, or the grant is simply revoked and re-issued). No host-wide durable key or address ever exists, so counterparties can neither link two grants to one host nor track an endpoint as an identity — servers honor credentials, not identities. Cross-wallet delegation itself is still aspirational today. But a 24/7 box is not always wanted, and a single self-hosted home is a single point of failure for everyone who delegates to it — so a managed option sits alongside it (Hosted authority homes, next).
Hosted authority homes — HostedW3WalletService. Self-hosting is preferred, but some authorities genuinely must be reachable 24/7 and are a poor fit for a user's own machine, and a single always-on home is a single point of failure for every wallet that delegates to it for privileged operations. HostedW3WalletService is the managed option: a service where a user provisions one or more managed wallets on managed hardware and grants authority to them exactly as to any other helper. It buys two things — (1) always-on without running your own box, and (2) availability isolation: spreading authorities across several independent hosted wallets on separate servers means one going offline does not strand the others' delegators. Crucially it is managed hosting of independent, user-owned wallets — not a central authority: each hosted wallet is its owner's own source of truth, created/configured/revoked from the owner's coordinator console, and the service authorizes nothing itself (it is emphatically not an authorization analog of the settlement custodial hub). The honest trust cost, stated plainly: the operator can technically read anything a hosted wallet holds or does — it is their hardware, and no software-only design changes that. The mitigation is structural: a hosted wallet is a satellite, never the primary — it holds only the specific authorities that genuinely need 24/7 reachability, each as its own per-capability, source-rotatable secret, and never the device-group sync key, the recovery key or backup, the hub credential, or the primary grant ledger — so operator compromise is bounded to the secrets deliberately placed there. Tenant isolation is one OS process/container per hosted wallet on top of the SJVM sandbox (which guards plugin↔plugin within a wallet); confidential computing / TEEs are a deferred hardening upgrade that would shrink the operator out of the TCB, not a v1 requirement. Recommendation: self-host where you can; reach for HostedW3WalletService for authorities that must be 24/7 or need availability isolation.
Recovery — encrypted backup to your own helper. Multi-device sync is the everyday recovery path; for last-device loss the wallet keeps a continuous, end-to-end-encrypted backup on the user's own helper/cloud daemon, openable only by a user-held recovery key. Restore = install a fresh daemon and unlock the backup with the recovery key, recovering capabilities, secrets, the grant ledger, pool/lease state, agreements, and the hub credential (so w3coin balances — held on the hub regardless — become accessible again). The recovery key is the one durable secret. It is protected by the user's choice of passphrase-derived key, hardware token, or — the recommended default — k-of-n Shamir split escrow across the user's own device group (and optional trusted guardians), which alone defends against both loss and theft with no central authority. Defaults: 2-of-3 (primary device, always-on helper, one offline share — paper or a password manager), scaling to 3-of-5 with guardians; the rule of thumb is n = the independent locations you actually have, k = a majority, defending loss and theft at once. A guardian is just another wallet — a friend's, or one of your own hosted wallets — holding an opaque, mailbox-delivered share bound to nothing; retrieval is an out-of-band human act, which is the anti-theft property. Losing the recovery key (or k Shamir shares) is, for self-custodied secrets, unrecoverable by design — the unavoidable cost of no-central-authority + no-durable-identity — mitigated because the key is needed only for last-device loss (multi-device sync covers the rest) and because Shamir turns "lost" into "lost k shares, not one." Because hub-held balances are irreplaceable (capability secrets, by contrast, can be re-established at their source authority), the hub credential carries its own independent recovery path: at account creation the hub issues a one-time offline recovery code — a second, possession-based credential the hub accepts for exactly one operation, rotate this account's credential — stored offline (and optionally Shamir-split itself). It authorizes nothing in the capability layer, so it stays confined to settlement without reintroducing identity, and balances survive even recovery-key loss.
Design: enforcement and trust roots
Trust roots — self-authority now, federation-ready. Each resource server is the sole authority for its own resources and trusts its own signing key as the root for caps over them; a verifier checks "signed by the authority that owns this resource." Verifiers learn that key by resolving it from the authority's own url:// endpoint (the token carries the authority's reference) and caching it as soft state — re-fetched on an unknown-key miss, and cached/served per-service by the PMS. The authority rotates gracefully by publishing a new key signed by the old (a rotation chain a verifier can follow), with outstanding caps surviving through their lease overlap; a forced rotation after key compromise is an epoch bump that invalidates everything under the old key and forces re-mint. Crucially, user→user delegation does not need federation: when Alice delegates her LambdaServer access to Bob, LambdaServer is still the authority — only the holder changed (and, for reference caps, Alice's daemon proxies). Federation — authority A's caps honored by a different service B — is deferred as a build item, but its trust shape is settled: acceptance is an explicit per-verifier allowlist ("for resource namespace X, accept authority root K"), operator-set and coordinated from the owner console like any other config — nothing global, no implicit trust; key discovery reuses the same url:// resolution and rotation chains; and the namespace-ownership "registry" role falls to UrlResolver's planned verified service ownership (DNS-like, signed-descriptor name resolution) rather than any new W3Wallet registry — one trust-root system across the ecosystem instead of two. Web-of-trust endorsement is rejected outright: it needs durable identities and a social graph, both non-goals.
Turnkey enforcement — the developer payoff. Every architecture layer drops in one reusable gate: identify the principal (origin header or process attestation), obtain its presented capabilities / group handle (opening a url:// connection to the daemon if needed), verify the required capability (itself or via the PMS), enforce any allowance (parking an unfundable draw until accrual funds it — see Design: spend and settlement), and emit an audit event — so requiring a capability on a ServiceServer RPC or a WUI route is a few lines. A reusable "Sign in with W3Wallet" flow is the WUI half. This turns "auth is a sub-project for every service" into "auth is inherited."
The owner console is a coordinator, not a central authority. The owner-facing frontend (WUI/GUI) is the management half of the same model: it binds to the user's own daemon(s) and helpers and lets them coordinate the fleet — which keys (capability JARs) are installed, who holds use, each grantee's allowance, the audit log, which device is the authoritative pool holder, per-device lease caps. It is established only with the owner-side admin surface (an admin extension of the spender capability surface), never a server everyone logs into — consistent with the non-goal of any central authorization source of truth. Because "manager" can itself be a capability role, coordination can even be delegated to a trusted colleague's wallet without centralizing anything.
Decision log
Settled, with rationale. (Forks resolved interactively across design rounds.)
- No durable identity. Authority is presented and validated best-effort; the user's daemon is the source of truth. Why: privacy, and it forces a clean object-capability model.
- Two capability representations — reference (call-home, hidden secret, live audit, instant revoke) and token (signed, leased, presented, offline-verifiable). Why: "gate every use" and "scale/offline" are genuinely different jobs.
- Delegation by reference (chains). Why: lets a grantor keep the secret, see every use, and revoke unilaterally — the three properties at once.
- Token format = macaroon-style. Why: holder-side offline attenuation via append-only caveats is structural.
- PMS is optional, best-effort, and service-run only. Why: it then sees only its own relationship's traffic (no new trusted party), and losing it costs a round-trip, not correctness.
- Revocation = lease + bounded blacklist + epoch bulk-revoke; renewal is the inverse lever. Why: bounds the blacklist and makes revocation routine.
- Scale via durable roles + soft working-set handles (both). Why: roles give structure; handles give a cheap ad-hoc fast path.
- Spend = asset pools drained by rate-limited allowances; no money-vs-credits distinction. Why: one abstraction; rate-bounded chains are offline-safe by construction.
- Multi-device synced; an always-on own device is the helper. Why: availability for reference caps + a natural backup/recovery anchor.
- Device-compromise blast radius is bounded by representation: reference caps need only a device-group key rotation (no re-issue), token caps a single epoch-bump re-mint. Why: the reference indirection means holders never held the secret, so rotating the device-group key (and any raw-resident secret at its source) suffices; offline self-contained tokens can't be re-pointed, so they re-mint under a new epoch — in one bulk op. Keeping authority in reference form makes device eviction a rotation with zero re-issue.
- Trusted helper holds a full per-capability secret copy, preferably on your own device. Why: only a full copy can execute offline; on your own device a full copy is still "you."
- Settlement is in scope, via w3coin (a universal, arbitrarily-divisible ledger unit) on a centralized custodial hub for v1. Why: fastest path to real settlement; the adapter abstraction allows a later swap to federated/decentralized. The hub is the one deliberately-central component.
- w3coin is a closed-loop, non-redeemable utility credit in v1: the only funding adapter is an operator grant/faucet, and there is no cash-out. Why: nothing redeemable crosses the boundary — no money-transmission/KYC surface, no clawed-back-top-up edge case, and the identity-free capability layer stays uncontaminated; real-money or crypto adapters can be added later without touching the spend graph.
- First settlement implementation is a deliberately-minimal placeholder ledger (a simple service + database), not yet secure/private/production-grade. Why: unblock building and testing the rest of the model against a real backend; security/privacy gaps are known and deferred, and the abstraction above it lets the placeholder be hardened or replaced wholesale.
- Leased-balance coordination (authoritative holder + reserved leases; hub no-overdraw backstop). Why: disjoint slices make double-spend impossible in the common case, bounded by the hub otherwise.
- Hub failover = HA of a strongly-consistent (consensus-replicated) ledger; an outage degrades only settlement/renewal/funding, never capability use or lease-bounded spend. Why: the hub only settles and is out of the authorization path, and no-overdraw forces linearizable balance updates (eventually-consistent multi-master is unsafe). The v1 placeholder stays single-node; federation is the long-term single-point-of-failure answer. Target guarantees are pinned — RPO = 0 (quorum commit), automatic failover, RTO ≤ ~30 s, 3-node quorum — with the consensus implementation choice delegated to the hub repository.
- Lease defaults: TTL 5 min renewed at half-life; slice ≈ 2 × (rate × TTL), capped at 10 % of the pool; offline budgets explicit and off by default. Over-promise resolves by deficit-carry — the pool goes negative, accrual/top-ups pay it down before any new spend, no new leases while in deficit, and the offending device's offline budget needs re-approval; sellers accepting expired-lease spend bear that risk themselves. Why: each party carries exactly the risk it opted into, and the arithmetic self-heals with no central party.
- Rate limits ecosystem-wide ride the spend model; allowances are multi-bucket. Every service is encouraged — as an advisory pattern, not a mandate — to enforce rate limits through pools/allowances rather than a bespoke throttle; capacity-only limits use a bound, zero-price asset class (e.g.
myservice:requests) with no settlement, and rate-limited operations require a presented capability (no anonymous access to gate-protected ops). A pool/allowance carries a set of(accrualRate, cap)buckets; a draw must satisfy all; attenuation only tightens. Why: one abstraction instead of per-service throttles; continuous accrual removes reset cliffs and their stampedes; real budgets routinely need several simultaneous constraints (per-second burst + per-day budget). The service-facing pattern is documented in architecture/RATE_LIMITS.md. - Oversubscription fairness = a 50/50 hybrid (half weighted max-min by granted rate, half equal-split among active consumers), enforced as lease-sizing at the pool's authoritative holder. Why: oversubscription is deliberate statistical multiplexing; under contention heavy consumers keep some earned priority while light consumers keep a guaranteed floor — one heavy spender can never starve the rest; lease-sizing at the existing coordinator needs no new component, at the honestly-stated cost that an oversubscribed allowance is not fully offline-capable under contention.
- An unfundable draw parks under a connection-scoped GC lease (the Observables pattern) and is funded by fair-share refill — never reject-and-retry. Abandoned draws are collected when their lease lapses; refill is FIFO within a consumer, hybrid fair-share across consumers. Why: parking eliminates retry storms and synchronized-reset dogpiles (the stampede defense) and degrades progress smoothly to the accrual rate instead of a hard stop.
- Spend-bearing tokens are paid-at-mint, refund-on-report. The full reserved slice settles to the seller at mint (cash-like: zero seller counterparty risk, fully offline-capable); the seller's signed usage report refunds the unspent remainder; an unreported slice is simply spent. Why: offline acceptance must be safe for the seller; buyers bound their exposure by sizing minted slices, and agreements can make refund-on-report a term.
- Cancellation ↔ reclamation via signed spend agreements with an Active/closing/Delinquent/Closed state machine. Why: a bilateral, machine-checkable contract both sides can enforce. Closing is bounded by a seller-confirmation window term (default = the grace period, 7 days); on lapse the buyer's daemon unilaterally finalizes to Closed (unconfirmed) with the signed notice as the audit record — safe because funding already stopped and reservations lapse on their own.
- Dispute recourse = buyer hard-stop + signed audit record; no v1 arbitration or reputation. Why: spend is pull-from-the-buyer's-allowance, so cancelling funding structurally stops a non-cooperative seller; arbitration would need a central authority and reputation a durable identity — both rejected non-goals. An opt-in reputation/arbitration layer can be added on top later, never in the core.
- Recovery = encrypted backup to your own helper/cloud, unlocked by a user-held recovery key (default: k-of-n Shamir split escrow). Why: covers last-device loss while keeping the only durable secret a single user-held key; recovery-key loss is unrecoverable by design for self-custodied secrets (mitigated by multi-device sync + the Shamir threshold), but the hub credential carries its own independent recovery path so hub-held balances survive even that. Defaults: 2-of-3 (device, helper, offline share), 3-of-5 with guardians (k = a majority of independent locations); guardians are just other wallets holding opaque mailbox-delivered shares; the hub's independent path is a one-time offline recovery code valid only for credential rotation.
- Offline delivery: sender fan-out to 2–3 recipient-designated dumb mailboxes, retry until the recipient's signed ack, 30-day envelope TTL; recipient binding + E2E encryption are unconditional (no transport-only mode). Why: redundancy at the edges beats building a replicated mailbox system; the threat model already distrusts relays, and key-binding (first use = proof of possession) makes an intercepted envelope worthless.
- The mailbox is daemon-embedded — a built-in function of every wallet daemon, never a standalone service/server. Why: every natural mailbox host (your helper, a friend's wallet, a hosted wallet) is already a daemon, so a separate mailbox deployable would add an operational component without adding any capability; embedding keeps "any wallet daemon can be a mailbox" literally true, keeps self-hosting the free tier, and avoids a public mailbox endpoint that could ossify into an identity-like rendezvous point. (The mailbox store lives as an embeddable library the daemon hosts over its existing
url://surface.) - Reference capabilities find their authority home by per-grant rendezvous: each grant embeds a random
url://rendezvous name + a per-grant keypair; the daemon keeps rotating its peer-id and proves possession per grant. No host-wide durable key or address exists. Why: the only stable artifact is the credential relationship itself — servers honor credentials, not identities, and grants stay unlinkable across counterparties. - Native-process attestation is a per-OS ladder with strength tags: kernel peer credentials for instance identity; macOS/Windows code-signing identity and Linux binary hash for code identity; registrations are tagged with attestation strength, prompts show the tag, grants may require a minimum. Why: usable everywhere (including unsigned dev tools) while staying honest about what each platform can prove; mechanics belong to the daemon repo.
- Privacy bar = the cross-relationship invariant: no key, id, address, token, or session artifact is presented to more than one counterparty; within-relationship correlation is accepted; the hub is mitigated by multi-account partitioning + batched/netted settlement. Why: per-relationship artifacts are cheap and already pervasive in the design; per-use unlinkability (ZK/blinded presentation) is heavy cryptography v1 doesn't need and is deferred.
- Storage is bounded by default: audit 90 d rolling (+ size backstop; the Event Log is the archive path), closed agreements 1 y then a compact summary, hub transaction detail 1 y beneath signed balance checkpoints; the blacklist was already structurally bounded. Why: unbounded append-only growth is a known production defect class; archival becomes an explicit choice, never a silent default.
- Shared infra is metered through the spend system it serves; self-hosting is the free tier. The PMS is the service's own cost; public relays/mailboxes/HostedW3WalletService charge via standing spend agreements; the v1 hub is operator-run and free with a settlement-fee hook (an ordinary allowance) reserved for federation; metering enforcement is off in v1. Why: dogfooding the model beats inventing billing machinery, and self-hosted defaults dissolve the bootstrap circularity.
- The capability catalog is a closed vocabulary carried in the grant: a closed wire-type set; per-parameter caveat dimensions (pin-exact, one-of, range, prefix) enforced by a generic matcher; optional per-function spend declarations; the signed, versioned catalog travels in the capability's public part; no central registry. Why: blind attenuation needs a decidable "within-caveat" check — a constraint matcher is trivial where subschema reasoning is a research project.
- Anti-replay / confused-deputy invariants: mandatory audience caveats (no bearer tokens exist), holder-bound tokens with proof-of-possession presentation, channel-bound session keys, and deputies re-present the caller's attenuated capability. Why: both attacks die by construction; the formal model and protocol spec are implementation-repo work.
- Time: durations are enforced on the risk-bearer's own NTP-synced clock with a bounded skew tolerance (default ±30 s); ordering uses HierarchicalClock timestamps; every implementation takes an injectable
Clock. Why: every expiry is enforced by the party bearing the risk of it being wrong, so manipulation only self-harms — no trusted global time source; logical clocks can't measure elapsed time, so they own ordering instead. - Trust = self-authority now, federation-ready. Why: v1 simplicity without foreclosing ecosystem-wide capabilities; user-delegation already works without federation. Federation's eventual trust shape is settled: per-verifier explicit allowlists per namespace, discovery via the existing
url://key resolution, UrlResolver's verified service ownership as the namespace registry, web-of-trust rejected (needs durable identities). - Authority keys are published at the authority's
url://endpoint and cached (soft-state, PMS-served); rotation is a signed rotation chain (graceful, lease-overlap) or an epoch bump (forced). Why: reusesurl://resolution, lease, and epoch — no new PKI; verifiers always reach a current key, with a source-of-truth re-fetch on a miss. - Hosted workloads are a first-class principal kind; platforms provision capabilities into the code they host. A delegation chain user → platform → workload, attenuate-only, instance-scoped and lifecycle-bound (auto-revoked on reap); the reference form is preferred so the secret never enters an untrusted host. Why: hosted code (ContainerNursery apps, LambdaServer functions, kompile tests/build rules, agents) must act with secrets it never sees, and AiCliHostSupervisor already proves the shape for the agent case. The binding is a fresh per-instance keypair injected at spawn (audience-bound credential; proof of possession defeats co-tenant replay), the platform signs the instance attestation, and lifecycle rides short-lease renewal; multi-tenant provisioning is reference-only by default, embedded JARs by explicit owner opt-in.
- Capability JARs are sandboxed plugins. The wallet is a generic host that knows nothing about a plugin's domain; guest JARs run under an SJVM sandbox seeing only their own secret and declared egress, and declare a typed catalog for blind attenuation and UI. Why: the wallet source must contain no service-specific code, and loading guest code beside every secret demands isolation against cross-capability theft and host attack. Egress and resource limits are manifest-declared and structurally enforced (no ambient network — only an injected allowlisted client); approval is consent to the manifest, plugins are never trusted beyond it, and manifest-changing upgrades re-prompt with a diff.
- Authority-home hosting: self-host preferred, HostedW3WalletService as a managed option; the owner console coordinates, never centralizes. A user's own always-on helper is the recommended default; HostedW3WalletService offers managed wallets on managed hardware for authorities that must be 24/7 or want availability isolation across independent hosts. The management frontend binds to the user's own daemons via the owner-side admin surface. Why: keeps "no central authority" intact — HostedW3WalletService is managed hosting of independent user wallets, not an authorization authority — while making "run your own service" just "run your own helper"; live audit/revoke/metering require something always-on, and a fully-offline posture trades down to token caps. The trust cost is stated honestly: the operator can read anything a hosted wallet holds, so hosted wallets are satellites (24/7 authorities only — never the sync key, recovery key, hub credential, or primary ledger), isolated one OS process/container per tenant atop SJVM, with TEEs as deferred hardening.
- No cross-asset FX. Each asset class is its own pool and flow.
Deliberate deferrals
Every design question previously parked here is settled in the Decision log and woven into the sections above. What remains is deliberately postponed or delegated — recorded as decisions, not questions:
- Real-money / crypto funding adapters. v1 w3coin is a closed-loop, operator-granted credit with no cash-out; purchase and redemption adapters (and the regulatory machinery that attaches to them) are future work behind the funding-adapter abstraction.
- Federation (the build). Its trust shape is settled (per-verifier allowlists; UrlResolver verified names as the namespace registry); actually honoring a peer authority's capabilities is not scheduled.
- Cryptographically-private presentation and settlement. Per-use unlinkable (ZK / blinded) presentation and e-cash-style settlement are future upgrades behind the existing abstractions; v1's bar is the structural cross-relationship invariant.
- TEE / confidential-computing hardening of HostedW3WalletService. The satellite posture and per-tenant process isolation are the v1 answer; shrinking the operator out of the TCB is future hardening.
- Delegated to implementation repositories: the hub's consensus implementation (its guarantees are pinned here), the capability-catalog wire format, per-platform OS-attestation mechanics, and the formal model / protocol spec of the anti-replay invariants.
Scope and non-goals
In scope: the two capability representations and their interop; reference-capability delegation chains with per-hop attenuation, audit, and instant revocation; macaroon-style token capabilities with offline verification; a service-run, best-effort PMS (validation, blacklist, renewal, grouping, session secrets); durable roles + soft working-set handles; lease-based revocation with bounded blacklists and epoch bulk-revoke; spend as asset pools drained through rate-limited, multi-bucket allowances with offline-safe chains, best-effort multi-device leasing, hybrid fair-share arbitration of oversubscribed pools, and park-until-funded exhaustion behavior; real settlement via a divisible w3coin asset class on a v1 custodial hub with pluggable funding adapters; standing spend agreements with a cancellation/reclamation state machine; offline delivery via daemon-embedded mailboxes + an ephemeral receiving key; multi-device sync with own-device helpers; encrypted-backup recovery; native-process and agent principals with OS-attested identity; hosted-workload principals with platform attestation and platform-provisioned, instance-scoped, lifecycle-bound capabilities (ContainerNursery apps, LambdaServer functions, kompile tests/build rules, agents); lazy permission prompts; self-authority (federation-ready) trust roots; capability-JAR plugins executed under an SJVM sandbox with domain-blind, schema-driven attenuation and UI; self-hosted authority-home helpers (with HostedW3WalletService as a managed option) and an owner/coordinator console (never a central authority); and turnkey per-layer enforcement.
Explicit non-goals: a central authorization server as a source of truth (settlement is the one deliberate central exception, and it does not authorize); a durable public wallet identity / login; server-side per-user ACLs as the primary model; passwords; any requirement that a resource server hold durable per-user grant state; cross-asset FX; cross-service capability federation in v1 (designed-for, not built). Milestone/sequencing breakdown is also deferred.
Relationship to other workstreams
- LambdaServer — capability-gated invocation, per-caller allowances, and the canonical "many capabilities" scaling case driving roles + handles; plus provisioning capabilities into hosted functions so a function calls an external API under an injected secret it never sees (see Capability provisioning).
- ContainerNursery — provisioning capabilities into hosted apps, so a deployed app reaches external APIs through injected Proxy/reference capabilities rather than ambient keys in its container environment (see Capability provisioning). It is also a candidate substrate for HostedW3WalletService's always-on managed wallets (kept warm rather than slept).
- Kotlin Build (kompile) — provisioning capabilities into tests (and possibly build rules) so a test reaches credential-bearing services without secrets in test source or the test process; and a capability to write the shared build cache (the cache-poisoning lever in that workstream's open questions).
- SimpleFileSystem — per-owner quota and delegated, revocable file-access capabilities; the standing-storage spend-agreement example.
- GithubProxy (a Git sub-area) — the canonical consumer of the Proxy / capability-JAR runtime with a real secret behind it: a GitHub API key becomes a wallet-held capability, use is delegated without view, and so much of the "service" dissolves into the wallet that GithubProxy reduces to a GitHub capability JAR plus generic always-on daemon hosting. Exercises delegation-by-reference (cross-wallet grants), turnkey enforcement (the four-capability gate), and GitHub's per-token budget metered in-daemon as a rate-limited asset pool (the reference-cap case needs no external coordinator).
- UrlResolver HTTPS Transport — carries this workstream's capability carrier unchanged when a service is hosted behind HTTPS: references and tokens stay in the RPC frames (never an HTTP header), and a channel-bound session key binds to the end-to-end session between the two peers, not to a TLS connection that ends at a platform's front end. Token capabilities, which verify offline, are the natural fit for services that scale to zero.
- Event Log — capability-gated append permissions, and the natural sink for the reference-capability invocation audit stream.
- HierarchicalClock — supplies the ordering half of the settled time model: revocation-before-use, epoch propagation, and audit ordering ride
HierarchicalTimestamps, while durations (leases, expiry, grace periods) are checked against the enforcing party's own wall clock (see Design: validation, revocation, and scale). - Agentic swarms — agents as principals holding scoped, auditable, revocable authority and bounded spend instead of shared ambient credentials; the AiCliHostSupervisor design is the worked example of capability provisioning into a hosted workload (an agent conversation).
- NetLab — the network-topology lab where capability flows are exercised end-to-end across NATs: a single container running a browser + the W3Wallet extension + the W3Wallet daemon (extension↔daemon over localhost) placed behind a NAT, talking to a public host that runs a web app and the W3Wallet PMS (which doubles as the in-topology
url://bootstrap node), with Playwright driving the browser via NetLab'sexec(). Reachability is favorable in the demo direction: the NAT'd wallet initiates the outbound connection to the public PMS, so no inbound hole-punch is needed; the reverse path (reaching a daemon that is itself behind a NAT — e.g. a reference capability whose issuer is NAT'd) leans on UrlResolver's already-tested relay fallback. Swapping the public host's image/command gives two variants — the JavaScript demo (client-sidewindow.w3walletonly, so no server-side capability check) and the Java-backend demo (the one that actually verifies capabilities across the NAT). A working first cut of both variants already exists inW3WalletTests/browser-nat-e2e: it is the daemon-enforced path — the public host runs only the web app and the NAT'd node's daemon is the authority home reached overurl://(the PMS-on-the-public-host version follows once the PMS is built, still aspirational). Most of the hardening has since landed (tracked in the NetLab workstream): the suite is now fully hermetic — both switches internal, running its own in-topology libp2p relay with no path to the public mesh; the maintained browser-host image is wired in as an opt-in (W3W_BROWSER_HOST_IMAGE); and host→client artifact retrieval pulls Playwright failure traces back out via NetLab'sdownloadFile. Both variants run in CI onnetlab-cli 0.0.26. What remains is refinement: driving the spec via NetLab'sexec()exit code (now available) instead of theE2E_RESULTlog marker, and deeper assertions than coin count.
Graduation
A living design-and-scope document. It graduates from "vision" when the design is decomposed into concrete workstream tasks and at least the token-capability + service-run PMS path and the reference-capability delegation model are adopted by another component-1 primitive — at which point it is no longer tracked as a workstream here. Milestones and sequencing are captured separately.