arizuko

arizukoreference › Store schema

schema

Every table arizuko keeps is an entity: one item per row, addressed by a (partition, sort) coordinate, with the rest of the row as string attributes. One DynamoDB-protocol server per instance serves them, named by DYNAMODB_URL, and nothing above the driver knows which server it is.

The coordinate is what a read costs, so each entity's coordinate is argued in the file that writes it. store/entities.go names the set each owner holds.

1. Four owners

An owner is a daemon and the tables it alone creates. On a served instance the tables are named <owner>.<entity> and each daemon signs with its own access key, so a policy can bound what it reaches; on a file instance each owner gets one bind-mounted directory. Either way a daemon reads another owner's tables only where this page says so.

ownerdeclared inholds
routdstore/entities.gomessages and their indexes, routing, grants, secrets, scheduling, packages
runedruned/runstorespawns, session log, circuit breaker
authdauthd/authstoresigning keys, accounts, refresh tokens
onbodstore/entities.goonboarding, gates, invites

audit_log is in all four. One declaration lives in audit, every owner creates it, and each serves its own read-only at GET /v1/audit; /dash/audit/ merges those four answers rather than reading anybody's tables.

2. routd — messages

entityaddressed bycarries
messagesthe chat, then the instant plus the message idsender, content, verb, source, attachments, topic, routed_to, errored, is_observed, turn id. The id is in the key because two messages can share an instant inside one chat.
message_foldersthe folder, then the message's own positiona pointer to the message. Routing is dynamic, so the folder a message reached is written when routed_to is stamped and never recomputed.
message_folder_countsthe folderthat folder's message count, so the usage page reads items instead of partitions
message_activityone constant partition, then the message's positionthe newest messages across every chat — the one read that has no coordinate of its own
message_deliverythe delivery status, then the message's positionattempts. sent is the absence of an item, so the entity holds only what still owes a delivery.
chatsthe chat jidagent_cursor (the dispatch watermark), sticky group and topic
sessionsfolder and topicsession id, parent topic, fork instant, observed_cursor
chat_reply_statejid and topiclast reply, engagement window and the folder holding it
chat_proactivethe chat jidthe instant that chat last spoke unprompted — the cooldown
system_messagesthe folder, then the instantone queued system event

Ranked search is not a table. routd keeps an in-memory BM25 index over the messages entity, walked from the store at boot and fed by its own writes; see find_messages.

3. routd — configuration

These thirteen are the registered resources: each has a REST face, an MCP face, and a row in arizuko export.

entityaddressed bycarries
groupsthe foldername, product, model, container_config (mounts, timeout), daily cost cap, open flag, observe window
routessequence, target and matchthe rule. The sequence is padded so a read comes back in precedence order; the id an operator deletes by is minted through counters.
route_tokensthe sha256 of the tokenjid, owning folder, context, kind (route delivery bearer or pair identity link). The digest is the key; the raw token is never stored.
web_routesthe path prefixaccess (public / auth / deny / redirect), redirect target, owning folder
proxyd_routesthe pathproxyd's reverse-proxy table. proxyd owns every write and serves it on its own /v1.
aclprincipal, action, scope, params, predicate and effectnothing else — those six fields are the grant
acl_membershipchild and parentwho added the edge and when. Roles and jid→sub claims share it.
secretsscope kind, scope id and keythe value, sealed AES-256-GCM under SECRETS_KEY
secret_use_logthe instant a tool call resolved onespawn, caller, folder, tool, key, outcome, latency
network_rulesfolder and targetthe per-folder egress allowlist. The folder leads because it is also the purge scope.
scheduled_tasksthe task idowner, chat, prompt, cron, next run, status, context mode
pending_actionsthe call idone tool call held for a human verdict
installed_packagesfolder and name (empty folder = instance-wide)source, revision, the identities the package owns, per-asset hashes
github_sourcesthe source keygitd's webhook delivery secret and the folder it delivers to

4. routd — the rest

entityaddressed bycarries
turn_contextthe turn idfolder, topic, chat, trigger sender, run id, state, the hash of the gateway key that turn was metered with
turn_resultsfolder and turn idsession, status, when it was recorded. Create-if-absent is what makes one report one delivery.
task_run_logsthe instant a task rantask, duration, status, result, error
user_profilesthe person's subthe username their folder is named after, their daily spend cap
group_watchersobserver and sourcenothing — the pair is the fact
pane_sessionsteam, user and threadthe Slack assistant pane's channel and context jid
countersthe name of what it numbersthe count. There is one, routes.id, moved by a compare-and-swap.

5. runed, authd, onbod

entityaddressed bycarries
spawnsone constant partition, then the run idfolder, topic, container, session, kind, state, outcome, exit code, timestamps. A guarded write names one item, which is why the run id is the whole address.
session_logthe folder, then the instant it startedsession id, end, result, error, message count
circuit_breakerthe folder, both halvesconsecutive failures and the last one. Every operation is a point operation on one folder.
signing_keysone partition per daemon, then the kidthe ES256 keypair and its retirement
signing_key_slota single itemwhich kid signs now. One item is what makes two signers unrepresentable.
auth_usersthe subname, created
oauth_identities<provider>:<provider sub>the account that login belongs to
refresh_tokensthe token hashfamily, sub, scope, audience, issue, expiry, use and revocation instants
refresh_familiesthe family idone login lineage
onboardingthe chat jidstatus (awaiting_messagequeued / approved / refused), gate, sub, timestamps. No credential: the link is a pair row in route_tokens.
onboarding_gatesthe gatedaily limit, enabled
invitesthe invite reftarget glob, issuer, expiry, max uses, used count
invite_redemptionsthe person, then the instantone unspent subgroup redemption. The point delete is the claim.
audit_logthe instant, at fixed width plus a suffixcategory, action, actor, resource, scope, surface, outcome, duration, turn, folder, instance, request, source ip. The id is the sort key and is the instant, so ordering and the page cursor are one value.

6. What an entity is not

7. Who writes

The owner daemon writes its own tables. Two readers reach across on purpose: proxyd resolves a route token and reads acl per request, because a login-time snapshot would keep a revoked grant working; dashd reads routd's and onbod's tables for the pages it renders. Everything else goes through the owner's /v1. A mutation carries its audit_log row in the same batch, so a refused write records nothing and a recorded row happened.