Reference

Access

Applications define authentication and permission rules. Yttrium binds identities, replicates declared policy inputs, enforces decisions locally, and delivers private derived views. There is no per-action authorization RPC and no freshness heartbeat.

Boundary

Application ownsYttrium provides
Identity provider, principal contents, origins, logout/refreshAuthenticated upgrade routing and bound session context
ACL shapes, roles, grants, selectors, trusted permission codeDeclared policy replicas and input/output enforcement
Safe UI permission information and its shapePrivate subscription views, replacements, revisions
Meaning of repeated user actionsExisting transport retries, authorized before processing

Interfaces live in src/access/. A client id is never authority. Hello ids are transport bookkeeping. Directed replies go to the originating socket even if another socket claims the same client id.

createYttrium

const application = createYttrium({
  authenticate: (request, env) => authenticateWithYourProvider(request, env),
  serviceSession: (sourceDomain, policyDomain, env) =>
    serviceIdentityFor(sourceDomain, policyDomain, env),
  access: (domain) => ({
    principalKey: (principal) => stableAccountOrServiceKey(principal),
    policyDependencies: () => policyDomainsFor(domain),
    authorize: (context) => can(context),
    sessionValid: (session) => sessionStillValidLocally(session),
    privateViews: viewsFor(domain),
    onDecision: (event) => observeDecision(event),
  }),
  serverOptions: (domain) => ({ shape: shapeFor(domain) }),
  initialize: (domain) => initialMutations(domain),
});

authenticate returns { principal, expiresAt? } or null. WorkOS, Better Auth, Supabase, cookies, or another scheme can sit behind that hook. The application validates permitted origins and must not treat a browser-supplied identity as proof.

principalKey must be stable across issuers and tenants. Browser storage must use the same account scope; this hook does not configure ClientStore automatically. Principal identity is not a retry identity.

authorize, sessionValid, private derivation, and validation are synchronous trusted server code. They must be bounded and perform no network I/O. Missing policy, exceptions, and non-boolean decisions fail closed as unavailable. An explicit false is denial.

Authorization context is { principal, domain, state, policies, request }. Request kinds are subscribe, receive (raw inbound frame), send (raw outbound frame), and private_view. Protect policy, author, owner, and selector fields explicitly. Generic write permission does not imply they are mutable.

Host session lifecycle

The public gateway constructs a fresh internal request and replaces any caller-supplied trusted-context header. Only trusted server code with the Durable Object binding constructs service context. The DO verifies its routed domain, then binds the principal to a hibernation attachment.

Enforcement covers subscription admission, all client protocol lanes, shared output per recipient, and private-view disclosure. Required inputs are checked before application policy. Every protected operation rechecks expiry and local session validity, including outbound updates.

Authentication refresh is a new authenticated connection. There is no in-band message that changes a socket's principal. Expired credentials close with 4001, denied subscriptions with 4004, and temporarily unavailable policy with 4003.

A denied mutation receives a terminal nack; the outbox discards it. Missing policy, hook failure, and expired credentials close or pause without nacking pending edits. Subscription denial prevents reading and stops automatic reconnect until the application retries.

A cold domain may accept an authenticated transport while policy synchronizes; it sends no protected content until the barrier and authorization succeed. A warm denied upgrade can return HTTP 403 immediately. Socket establishment alone does not grant read access.

ACL domains

An ACL is ordinary shaped Yttrium data. Policy edits use the same authorized mutation path as content. Each content DO subscribes to declared policy domains as its own service principal; the policy domain dials the feed and the content DO accepts it (see Host). The ACL authority checks that identity and scope. It never borrows a user's permissions or forwards internal ACL state to ordinary browsers. Administrators may separately subscribe to the ACL for editing.

PolicyReplica is a quiet, read-only Session. Its cache lives in the content DO's policy_cache SQLite table, outside the content log. A cached snapshot is unavailable until a fresh subscription synchronizes. The replica becomes ready only after shapes and a contiguous apply through synced.commit. A gap, malformed frame, or service denial removes readiness. Callbacks from replaced sockets cannot revive it.

The first implementation requires fully materialized policy inputs: record/tree shapes with keep_last: null. Elided, external, and mounted policy bodies fail closed. Multiple dependencies are supported, but their heads are not an atomic cross-domain snapshot.

Normal propagation targets below one second, with five seconds as an operational target, not a hard bound during silent network failure. A stalled socket can retain stale authority beyond that target.

Private views

definePrivateView({
  name: "availableActions",
  shape: availableActionsShape,
  dependencies: ["ws/acme/acl", "resource"],
  derive: ({ principal, state, policies }) =>
    describeActions(principal, state, policies.get("ws/acme/acl")),
  validate: (value) => validActions(value),
});

Ordinary clients receive derived permissions, not the ACL. Derivations run server-side. Clients cannot write views. Delivery is a full replacement frame. Empty ready values never stand for unavailability. Denial removes the view; derivation failure makes it unavailable.

The playground reads Session.privateViews.get("availableActions") to enable controls. Applications may select this name in cachePrivateViews and read privateView(name, { allowCached: true }) for stable UI during reconnect. Cached hints are marked cached and contain no subscription or revision; removal and denial clear them. The host still evaluates the submitted operation independently.

Demo policy

src/example/application.ts is one application, not a library invariant:

YTTRIUM_DEMO_AUTH=true enables ?as= / workspace=. Same-origin only. The live Worker currently has this on.

Limits

Turnkey identity-provider adapters, production login UX, strict staleness modes, and policy hydration remain application or follow-up work.