Encrypted values, visible metadata, narrow authority.
Skarbiec protects field values with recipient encryption and limits machine use with exact capabilities; host integrity, key custody, transport, and backups remain operator responsibilities.
Protected and visible#
Field values are GPG ciphertext for the item's recipient set. Item ids, kinds, tags, recipients, revisions, grant names, and capability shapes are metadata and remain visible to the owner and relevant administration surfaces.
The item index is grant-filtered on every route#
The broker's versioned routes authorize every operation against an exact structured capability, and the two unversioned aliases for the item index now answer through the same gate. The metadata listing and the item-count summary apply the consumer grant and the per-item filter that the versioned item listing applies. A caller presenting no usable consumer grant is refused with 403 and a body naming a required consumer grant. A caller presenting one reads only the items its read grants cover, and the summary counts that filtered set rather than the vault.
Those two routes were open until recently, and the reason they closed is worth stating rather than editing away. This documentation called them compatibility routes for existing callers, and they were left ungated on that basis. A search across every product in this workspace found no caller of either — every client reaches the index through the versioned listing — so that sentence was the only thing holding them open. They stay reachable rather than deleted, because a caller nobody found reads a refusal that names the missing grant and says what to fix, where a not-found answer is indistinguishable from a wrong port or a dead daemon.
The loopback bind is still the transport boundary, and it is still the operator's decision whether to publish the port through a tunnel, a reverse proxy, a forwarded socket, or a container port map. It is no longer the only thing in front of the inventory and the access graph. Whatever reaches the port has to name a consumer and present a bearer the vault knows before it reads a single item, and it then reads only what that grant covers.
- All three routes that hand back the item index share one authorization, held in one place so the aliases cannot drift from the gate they are meant to match.
- The item-count summary is named for audit but returns no audit data. It returns one count of the non-trashed items this caller is allowed to see, and nothing else. The audit journal is a separate operator route.
- The liveness and readiness routes remain unauthenticated, and they touch no item data — they report process liveness, canary decryption, and cryptographic executor occupancy only.
Deliberately narrower surfaces#
- The MCP server exposes metadata and audit operations, not raw item reads, token minting, rotation, or export.
- The browser extension receives neither a vault bearer nor an owner key; the native host rechecks origin and item domain policy.
- One-use acquisition deletes the capability hash before returning the field, so replay fails even after a successful response is interrupted.
Reading requires possession#
Decrypting any item requires a recipient or recovery private half held in the local keyring. When that key is passphrase-protected, Skarbiec supplies the unlock phrase for a single decrypt over stdin, never on argv and never to disk. With no key present, or no unlock phrase for a protected key, decryption fails closed rather than prompting.
An attacker who copies the vault file alone gains nothing: they also need the private half and, if it is protected, the phrase that opens it.
What the browser boundary still allows#
The native-messaging manifest accepts only the extension id derived from the release signing key, the release build refuses a signing key whose derived id differs from the pinned one, and managed policy force-installs that same id. The residual exposure is stated rather than implied: a compromised extension can request eligible login items, and it cannot read non-login items or mint a broader grant.
Tamper evidence#
The journal is one JSON line per event under a directory forced to owner-only access, with the file itself owner-read-write. Each line carries at, op, extra, prev, and hash. The prev member is the previous line's hash and is empty on the genesis line; hash covers the previous hash together with that line's own fields, so a retroactive edit breaks every hash after it. The predecessor is read from the journal inside the append lock and never from a cached copy.
Every append is part of the operation rather than best-effort background work: the call returns only once the entry is durable. The extra member names identifiers only — item, field, consumer, kind, revision — never a value. An item write additionally records the writing process, its parent, and both the stored tag count and the tag count the writer passed, so a tag set that falls to zero names the writer that emptied it.
verify-chain checks three properties apart and stops at none of them: linkage, that each ordinary line names the line before it; epoch signature, that each checkpoint opens under GPG and matches the checkpoint stored in the journal line; and digest, that each line's fields still hash to the hash it carries. audit-epoch-start preserves damaged history and opens a new signed period; it never edits an existing line.
What the journal does and does not record#
The journal is the cross-item timeline of accepted operations, and its scope is worth stating exactly, because evidence nobody wrote down is not evidence. Each stored revision appends one item write, and rolling an item back to an earlier revision appends another, because a rollback stores a revision rather than reactivating an old one. Returning an item to owner control is recorded, and so is every grant, recipient, acquisition, capability, lifecycle, route, synchronization, donation, policy, recovery, and injection operation.
The exception is stated rather than implied. The owner-side trash, restore, and purge commands change the envelope only and append nothing, and the operator console reaches them through those same commands, so it appends nothing either. A consumer deleting through the broker is recorded, because that is a consumer spending a grant. So the journal proves what a vault stored, and for an owner acting locally it does not prove that a record was later trashed or removed. An item's own superseded revisions remain inside the item until a purge, and a purge removes that history without rewriting a single journal line.
Write-once receipts are a separate, external surface. Point the receipt directory and checkpoint at a write-once filesystem or bucket mirror and doctor verifies that both exist: unset is reported as not_configured, both present as pass, and configured but absent as fail. not_configured is deliberately not a failure — a fresh install has enabled no receipts, and calling that an outage is how a dashboard teaches its operator that red means nothing.
Limits#
A compromised host with a usable recipient private key can read what that key can decrypt. Skarbiec does not replace OS permissions, hardware-backed custody, TLS, firewalling, service supervision, backups, monitoring, or recovery drills.