Files
SeedPass/js/packages/test-vectors/fixtures/semantic.json
T
thePR0M3TH3ANandClaude Opus 5 39c4e45376 feat(semantic): port the retrieval index — and stop it writing secrets to disk
The "semantic (vector search)" entry in not_yet_ported was a misnomer. It is
not vector search and needs no model: MODEL_ID is "seedpass-token-overlap-v1",
and it scores by Jaccard overlap over tokenized metadata. That is why it could
be ported at all — there is nothing to embed.

Porting it surfaced two defects in the Python implementation.

**Stored secrets were written to a plaintext, 0664 file.** `_extract_text`
appended a key_value entry's `value` — the stored secret itself — and
`build()` wrote it to semantic_index/records.json in the clear, along with a
tokenized copy that leaked it just as thoroughly. The file was created at the
process umask, so on a default install it was group- and world-readable, next
to a vault that is encrypted and 0600. Anything that could read the directory
— a backup, a sync client, another user on a shared machine — got the secret
without the master password.

Fixed in Python (index the key NAME, never the value; chmod 0600 on both
files) and never done in TypeScript. The rule is worth keeping simple: nobody
searches for a secret they do not already know, so indexing one is pure
downside. MODEL_ID is bumped to v2 so an index built by the old code is
distinguishable — a v1 index on disk still contains the secrets and should be
rebuilt.

**Entry 0 was silently unsearchable.** The check was
`int(entry.get("id", 0) or 0) <= 0`, conflating "no id" with "id 0" — and
entry ids start at 0, so the first entry any user ever created was absent from
every search result with nothing to indicate why. Found by writing a test that
queried for it. Fixed in both implementations: skip an id that is absent,
unparseable or negative, which is what the check was reaching for.

The TypeScript port is wired into the CLI (`semantic build/status/search`),
the API (the /api/v1/semantic routes are now real rather than 501), and
writes through atomicWrite for a genuine 0600 inode. Fixtures come from
scripts/generate_ts_port_fixtures.py, which asserts the leaked value is absent
from its own output, so the fixture-drift job proves provenance the way it
does for every other fixture.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
2026-08-19 09:54:53 -04:00

326 lines
6.8 KiB
JSON

{
"description": "semantic index records and rankings",
"entries": [
{
"id": 0,
"kind": "password",
"label": "first-ever.example",
"notes": "the very first entry a profile creates",
"tags": [
"edge"
],
"username": "zero"
},
{
"id": 1,
"kind": "password",
"label": "bank.example",
"notes": "main current account",
"tags": [
"money",
"daily"
],
"url": "https://bank.example",
"username": "alice"
},
{
"id": 2,
"key": "DEPLOY_TOKEN",
"kind": "key_value",
"label": "deploy-token",
"notes": "ci pipeline",
"tags": [
"ops"
],
"value": "SECRET-MUST-NOT-APPEAR"
},
{
"content": "how to recover the bank account if locked out",
"id": 3,
"kind": "document",
"label": "recovery notes",
"tags": [
"docs"
]
},
{
"id": 4,
"issuer": "Fastmail",
"kind": "totp",
"label": "email-2fa",
"notes": "",
"tags": [
"email"
]
},
{
"fingerprint": "SHA256:abcdef",
"id": 5,
"kind": "ssh",
"label": "prod-server",
"notes": "production access",
"tags": [
"ops"
]
},
{
"id": 6,
"kind": "nostr",
"label": "social",
"notes": "",
"npub": "npub1example",
"tags": []
},
{
"id": 7,
"kind": "seed",
"label": "child seed",
"notes": "not an indexed kind"
},
{
"content": "",
"id": 8,
"kind": "document",
"label": "",
"notes": "",
"tags": []
},
{
"id": 9,
"kind": "password",
"label": "linked",
"links": [
{
"note": "see recovery notes",
"relation": "depends_on"
}
],
"notes": "",
"tags": []
},
{
"content": "skipped",
"kind": "document",
"label": "no id at all"
}
],
"fixture_version": 1,
"kind_filtered": [
{
"entry_id": 5,
"excerpt": "prod-server\nproduction access\nops\nSHA256:abcdef",
"kind": "ssh",
"label": "prod-server",
"score": 0.142857
}
],
"queries": {
"DEPLOY_TOKEN": [
{
"entry_id": 2,
"excerpt": "deploy-token\nci pipeline\nops\nDEPLOY_TOKEN",
"kind": "key_value",
"label": "deploy-token",
"score": 0.166667
}
],
"bank": [
{
"entry_id": 1,
"excerpt": "bank.example\nmain current account\nmoney daily\nalice\nhttps://bank.example",
"kind": "password",
"label": "bank.example",
"score": 0.111111
},
{
"entry_id": 3,
"excerpt": "recovery notes\ndocs\nhow to recover the bank account if locked out",
"kind": "document",
"label": "recovery notes",
"score": 0.083333
}
],
"bank account": [
{
"entry_id": 1,
"excerpt": "bank.example\nmain current account\nmoney daily\nalice\nhttps://bank.example",
"kind": "password",
"label": "bank.example",
"score": 0.222222
},
{
"entry_id": 3,
"excerpt": "recovery notes\ndocs\nhow to recover the bank account if locked out",
"kind": "document",
"label": "recovery notes",
"score": 0.166667
}
],
"email": [
{
"entry_id": 4,
"excerpt": "email-2fa\nemail\nFastmail",
"kind": "totp",
"label": "email-2fa",
"score": 0.333333
}
],
"first entry": [
{
"entry_id": 0,
"excerpt": "first-ever.example\nthe very first entry a profile creates\nedge\nzero",
"kind": "password",
"label": "first-ever.example",
"score": 0.181818
}
],
"nothing matches here": [],
"ops production": [
{
"entry_id": 5,
"excerpt": "prod-server\nproduction access\nops\nSHA256:abcdef",
"kind": "ssh",
"label": "prod-server",
"score": 0.285714
},
{
"entry_id": 2,
"excerpt": "deploy-token\nci pipeline\nops\nDEPLOY_TOKEN",
"kind": "key_value",
"label": "deploy-token",
"score": 0.142857
}
],
"recover locked": [
{
"entry_id": 3,
"excerpt": "recovery notes\ndocs\nhow to recover the bank account if locked out",
"kind": "document",
"label": "recovery notes",
"score": 0.166667
}
]
},
"records": [
{
"entry_id": 0,
"kind": "password",
"label": "first-ever.example",
"text": "first-ever.example\nthe very first entry a profile creates\nedge\nzero",
"tokens": [
"a",
"creates",
"edge",
"entry",
"ever",
"example",
"first",
"profile",
"the",
"very",
"zero"
]
},
{
"entry_id": 1,
"kind": "password",
"label": "bank.example",
"text": "bank.example\nmain current account\nmoney daily\nalice\nhttps://bank.example",
"tokens": [
"account",
"alice",
"bank",
"current",
"daily",
"example",
"https",
"main",
"money"
]
},
{
"entry_id": 2,
"kind": "key_value",
"label": "deploy-token",
"text": "deploy-token\nci pipeline\nops\nDEPLOY_TOKEN",
"tokens": [
"ci",
"deploy",
"deploy_token",
"ops",
"pipeline",
"token"
]
},
{
"entry_id": 3,
"kind": "document",
"label": "recovery notes",
"text": "recovery notes\ndocs\nhow to recover the bank account if locked out",
"tokens": [
"account",
"bank",
"docs",
"how",
"if",
"locked",
"notes",
"out",
"recover",
"recovery",
"the",
"to"
]
},
{
"entry_id": 4,
"kind": "totp",
"label": "email-2fa",
"text": "email-2fa\nemail\nFastmail",
"tokens": [
"2fa",
"email",
"fastmail"
]
},
{
"entry_id": 5,
"kind": "ssh",
"label": "prod-server",
"text": "prod-server\nproduction access\nops\nSHA256:abcdef",
"tokens": [
"abcdef",
"access",
"ops",
"prod",
"production",
"server",
"sha256"
]
},
{
"entry_id": 6,
"kind": "nostr",
"label": "social",
"text": "social\nnpub1example",
"tokens": [
"npub1example",
"social"
]
},
{
"entry_id": 9,
"kind": "password",
"label": "linked",
"text": "linked\ndepends_on\nsee recovery notes",
"tokens": [
"depends_on",
"linked",
"notes",
"recovery",
"see"
]
}
]
}