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.
| Store | Module | Size | Record | Write authority | Bound in |
|---|---|---|---|---|---|
| Snippet library | snippet.c / .h | 1540 B: 16 slots plus a 1024-byte packed source pool | name (1 to 16 chars) plus up to 512 bytes of Lisp source plus modified_ts | raw: (snippet-save NAME SOURCE) | cart tier only (nosh_lisp_bridge.c:4029) |
| REPL history | repl_history.c / .h | 1284 B: 16 slots of 80 bytes | one submitted line, chronological, deduplicated, saturating | implicit, on submit | C, no Lisp surface |
| Keystore | keystore.c / .h | 388 B: 8 slots of 48 bytes | id (28) plus fingerprint (12) plus packed kind / owner / origin / state plus strength | sanctioned 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 (pto pin,tabto view pinned only) and a preview toggle that shows an entry’s full contents while the list stays truncated. Assigned to Kommander.mcflyis the ranked-history-search reference, cross-linked from theclipsenotes 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.
3. The proposal, and where it holds
Section titled “3. The proposal, and where it holds”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-saveis 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_commitis that deposit, fired by a Keyring RESOLVE capture or a GENERATE.keystore.h:26records 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.
| Kind | Payload | Write authority | Display | Producer |
|---|---|---|---|---|
:lisp | Lisp source, ≤512 B | raw, player-authored | plain | REPL, nEmacs |
:key | none today (fields only) | sanctioned outcome | fingerprint shown, never a secret | Keyring GENERATE / RESOLVE |
:secret | a credential, concealed | sanctioned outcome | concealed until revealed | Keyring 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:
- A record with a body the deck deliberately does not print. A
:secretkind whose payload the list renders as a fixed-width run of block glyphs rather than its contents. - 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.
- 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.
7. Gaps this review found
Section titled “7. Gaps this review found”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:202specifies 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:129describes 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:138andnemacs.md:188.