Skip to content

Snippets, the keystore, and a concealed-payload record (2026-07-30)

A review of what the deck already has for snippets and snippet management, what the inspiration corpus offers, and an assessment of the proposal that Keyring’s password-manager behaviour is a specialized snippet type filing credentials in a concealed form. Companion to the text-entry surface survey; the finding in §6 bears on the model selection that survey is waiting on.

1. Three named-record stores already exist, and they do not know about each other

Section titled “1. Three named-record stores already exist, and they do not know about each other”

All three live as tagless byte blobs inside DeckState (runtime/src/types.h:595, :625, :637) with a typed view pinned by a static_assert in their own module.

StoreModuleSizeRecordWrite authorityBound in
Snippet librarysnippet.c / .h1540 B: 16 slots plus a 1024-byte packed source poolname (1 to 16 chars) plus up to 512 bytes of Lisp source plus modified_tsraw: (snippet-save NAME SOURCE)cart tier only (nosh_lisp_bridge.c:4029)
REPL historyrepl_history.c / .h1284 B: 16 slots of 80 bytesone submitted line, chronological, deduplicated, saturatingimplicit, on submitC, no Lisp surface
Keystorekeystore.c / .h388 B: 8 slots of 48 bytesid (28) plus fingerprint (12) plus packed kind / owner / origin / state plus strengthsanctioned only: keystore/commit!program tier only (keystore_screen.c:271)

docs/software/programs/repl.md:112 already states the relationship between the first two: snippets are the named, reusable layer; history is the chronological, deduplicated layer. The keystore arrived later (GWP-661) and its header cites the snippet library as the pattern it copies (keystore.h:19).

Three findings from reading them side by side:

The snippet library has no management surface at all. Grepping runtime/system-image/, runtime/programs/, and ui/ for snippet returns nothing. The four FFI primitives are the entire feature. docs/software/api-reference/editor-tools/nemacs.md:202 specifies SYS → Load as a list of saved snippets; nothing implements it.

The snippet library is unreachable from the REPL. snippet-save and its three siblings are bound in nosh_lisp_bridge.c, the cart FFI bridge. The System tier (sys_context.c) binds neither the snippet primitives nor the keystore ones, and the REPL runs in a System context. So the player-facing snippet library ADR-0002 committed to for v1 is currently callable only from cartridge Lisp, which is the one tier that has no reason to save operator snippets. The prompt-text "Snippet name:" demo inside the bridge (nosh_lisp_bridge.c:4081) is the only place the feature is exercised as an operator flow, and it is a bridge self-test.

The two stores solve opposite halves of the same problem well. The snippet library has the better storage model (a packed pool with in-place compaction, so a variable-length payload costs what it costs). The keystore has the better identity model (a set keyed by id where a re-commit updates in place, so capture is idempotent). Neither has the other’s half.

2. What the inspiration corpus carries, and what it does not

Section titled “2. What the inspiration corpus carries, and what it does not”

docs/influences/cart-inspiration-map.md assigns nothing to Keyring. The REPL’s five assignments are all Lisp-implementation research (fe, mal, cl-termbox2, and two language-theory sources): reference for the evaluator, none for managing saved work.

The pattern a snippet manager wants is in the corpus, assigned elsewhere:

  • clipse (docs/influences/inspiration/clipse.md) is a clipboard-history manager and the corpus reference for the list plus top-filter plus preview triad. Its own notes call that triad the most reused productivity pattern in the corpus. It also carries pin as an orthogonal item state (p to pin, tab to view pinned only) and a preview toggle that shows an entry’s full contents while the list stays truncated. Assigned to Kommander.
  • mcfly is the ranked-history-search reference, cross-linked from the clipse notes for match-highlight by inversion. Assigned to Kommander.

A clipboard manager is a snippet manager with a different producer. clipse is the closer reference for the snippet library than anything currently assigned to the REPL, and the pin-plus-preview pair maps directly onto what a snippet list needs: mark the handful of snippets in daily use, and see a 512-byte body without leaving the list. Nothing in the corpus covers a password manager, so the concealed-payload behaviour has no reference to lean on and needs one.

The proposal: Keyring’s password-manager behaviour is a specialized snippet type that files credentials in a concealed form.

Structurally this is correct, and the duplication is real. Both stores are a fixed-slot, name-keyed record table inside DeckState with a small typed header and a payload. Both hand-roll slot search, name or id validation, capacity refusal, and a count accessor. Both were sized against the same _Static_assert(sizeof(DeckState) <= 8192) ceiling. Together with repl_history they spend 3212 bytes on three implementations of one idea. A single store with a kind discriminant collapses the allocator, the lookup, the validation, and the persistence path to one place, and the eventual manager UI to one screen.

4. Where it breaks: the difference in write authority is deliberate

Section titled “4. Where it breaks: the difference in write authority is deliberate”

The two stores differ in who is allowed to write them, and the difference is deliberate.

  • Snippets are player-authored. The operator types the body. snippet-save is a raw primitive with no gate: name plus source in, stored.
  • The keystore is engine-authored. ADR-0040 §3 makes Deck State read-only to the player, written only as the earned consequence of a sanctioned outcome. keystore_commit is that deposit, fired by a Keyring RESOLVE capture or a GENERATE. keystore.h:26 records the intent in one line: there is no raw player write path.

Collapsing a captured key into a snippet type breaks one side or the other. If the merged record keeps the snippet’s raw write path, an operator can hand-author a key, and a key in the keystore at :state :held is exactly what skips a future crack (keystore_has_held, the reuse payoff called for in docs/plans/2026-06-27-keyring-crack-design.md §5). Typing yourself a key would delete the reason capture is rewarding. If the merged record instead inherits the sanctioned-write constraint, the REPL can never save a snippet, which is the whole feature.

So the merge is right at the level of the mechanism and wrong at the level of the record. The resolution that keeps both: one store, one allocator, one lookup, one manager screen, with each kind registering its own policy.

KindPayloadWrite authorityDisplayProducer
:lispLisp source, ≤512 Braw, player-authoredplainREPL, nEmacs
:keynone today (fields only)sanctioned outcomefingerprint shown, never a secretKeyring GENERATE / RESOLVE
:secreta credential, concealedsanctioned outcomeconcealed until revealedKeyring crack, bzbx fragment

A kind is then {name rules, payload cap, write authority, display policy}, and the capability boundary stays where ADR-0040 put it, enforced per kind rather than per store.

5. The concealed payload is the genuinely new capability

Section titled “5. The concealed payload is the genuinely new capability”

Nothing in the tree conceals anything. A grep for redaction, concealment, or masking across runtime/src, runtime/system-image, runtime/programs, and ui/ui returns only bitmask arithmetic. There is no display policy layer, and no component renders a value it declines to show.

There is also no secret to show yet. A keystore record carries a fingerprint for INSPECT and no body, so a key is only a handle. What a crack yields today is (dossier-commit host:<id>:cred) (keyring.md:122): the knowledge that a credential exists, filed in DOSSIER, where it enables CONDUIT pre-auth. The operator never sees a credential string, because there is not one.

The proposal therefore adds three things the deck lacks:

  1. A record with a body the deck deliberately does not print. A :secret kind whose payload the list renders as a fixed-width run of block glyphs rather than its contents.
  2. A reveal gesture, with the reveal being the interesting design question: momentary while a key is held (nothing persists on glass), rather than a toggle that can be left on.
  3. A use path that never renders the value at all: the operator selects a secret in Keyring, and CONDUIT receives it. Concealment survives because the value moves between programs without passing through the display.

Item 3 is what makes it a password manager rather than a text field with asterisks over it.

6. Why this bears on the pending text-entry selection

Section titled “6. Why this bears on the pending text-entry selection”

The connection runs in both directions, and the second direction changes a number in the survey.

Text entry is why the snippet library needs the work. Naming a snippet is the ADR-0016 §6 multi-tap use case, and snippet_name_valid is exposed in the header specifically so the FFI can reject a bad name before the multi-tap path spends pool space (snippet.h:210). A snippet manager is unusable until the name field is.

A secret store removes the deck’s second-hardest free-typing surface. The survey classifies CONDUIT as :free, full ASCII, 4 to 60 characters, on the strength of three things it types: endpoints, credentials, and remote shell commands. Endpoints already come from a closed dialer set. If credentials come from the store and paste without being displayed, then the only free ASCII CONDUIT needs is the shell command itself, carried substantially by session history. That moves CONDUIT from the two-surface :free tier to the :narrow tier and leaves the REPL, nEmacs, ECHO, and the OOBE handle as the only surfaces that genuinely need to spell anything.

That matters because it reweights the selection. A model chosen for four surfaces can be chosen on REPL merit; a model that also has to serve a credential field is under pressure to handle full ASCII quickly. It strengthens PALETTE-1ST as the :narrow default: a password the operator neither types nor reads is a record they select, which makes typing avoidance the feature rather than a compromise.

  • snippet-* is bound in the cart tier and absent from the System tier, so the REPL cannot save or load a snippet. The ADR-0002 v1 commitment is unmet in the one place it was for.
  • The snippet library has no management surface, and nemacs.md:202 specifies one.
  • Keyring has no inspiration assignments. clipse (list, top-filter, preview, pin) is the reference the snippet manager wants and is assigned only to Kommander; a password manager has no reference in the corpus at all.
  • snippet.h:129 describes the library as 1441 bytes over 26-byte slot headers while the struct is 1540 bytes over 32-byte slots, as the same header explains 100 lines earlier. The stale figure is the leftover of the packed-to-aligned change.
  • ADR-0016 §“Known Unknowns” #6 still leaves the REPL-to-nEmacs snippet handoff binding open, cited as open from both repl.md:138 and nemacs.md:188.