open source is social v-it.org
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add CID-derived cap-ref fallback for missing/invalid refs

New shared module src/lib/cap-ref.js provides hashTo3Words (SHA-256 →\n3 BIP39 words) and resolveRef, which returns the record's ref when\nvalid or a deterministic fallback derived from its CID.\n\n- skim, vet, firehose: use resolveRef so every cap displays a ref\n- ship: import shared REF_PATTERN instead of inlining\n- Add @scure/bip39 dependency for the English wordlist

+98 -3
+7
bun.lock
··· 10 10 "@ipld/dag-cbor": "^9.2.0", 11 11 "@noble/curves": "^1.8.0", 12 12 "@noble/hashes": "^1.7.0", 13 + "@scure/bip39": "2.0.1", 13 14 "bs58": "^6.0.0", 14 15 "commander": "^13.0.0", 15 16 "env-paths": "4.0.0", ··· 98 99 "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], 99 100 100 101 "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], 102 + 103 + "@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="], 104 + 105 + "@scure/bip39": ["@scure/bip39@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1", "@scure/base": "2.0.0" } }, "sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg=="], 101 106 102 107 "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], 103 108 ··· 266 271 "@jsonjoy.com/json-pack/@jsonjoy.com/buffers": ["@jsonjoy.com/buffers@1.2.1", "", { "peerDependencies": { "tslib": "2" } }, "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA=="], 267 272 268 273 "@jsonjoy.com/util/@jsonjoy.com/buffers": ["@jsonjoy.com/buffers@1.2.1", "", { "peerDependencies": { "tslib": "2" } }, "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA=="], 274 + 275 + "@scure/bip39/@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], 269 276 270 277 "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack/@jsonjoy.com/base64": ["@jsonjoy.com/base64@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw=="], 271 278
+1
package.json
··· 18 18 "@ipld/dag-cbor": "^9.2.0", 19 19 "@noble/curves": "^1.8.0", 20 20 "@noble/hashes": "^1.7.0", 21 + "@scure/bip39": "2.0.1", 21 22 "bs58": "^6.0.0", 22 23 "commander": "^13.0.0", 23 24 "env-paths": "4.0.0",
+4 -1
src/cmd/firehose.js
··· 3 3 4 4 import { loadConfig } from '../lib/config.js'; 5 5 import { CAP_COLLECTION } from '../lib/constants.js'; 6 + import { resolveRef } from '../lib/cap-ref.js'; 6 7 7 8 const JETSTREAM_URL = 'wss://jetstream2.us-east.bsky.network/subscribe'; 8 9 ··· 36 37 } 37 38 38 39 const message = event.commit?.record?.title || event.commit?.record?.text; 40 + const ref = event.commit?.cid ? resolveRef(event.commit.record, event.commit.cid) : null; 39 41 if (typeof message === 'string') { 40 - return `[${time}] ${operation} ${collection} from ${didShort} rkey=${rkey} — "${message}"`; 42 + const refPart = ref ? ` (${ref})` : ''; 43 + return `[${time}] ${operation} ${collection} from ${didShort} rkey=${rkey}${refPart} — "${message}"`; 41 44 } 42 45 43 46 return `[${time}] ${operation} ${collection} from ${didShort} rkey=${rkey}`;
+1 -1
src/cmd/ship.js
··· 6 6 import { loadConfig } from '../lib/config.js'; 7 7 import { restoreAgent } from '../lib/oauth.js'; 8 8 import { appendLog, readProjectConfig } from '../lib/vit-dir.js'; 9 + import { REF_PATTERN } from '../lib/cap-ref.js'; 9 10 10 11 export default function register(program) { 11 12 program ··· 28 29 } 29 30 if (verbose) console.log(`[verbose] DID: ${did}`); 30 31 31 - const REF_PATTERN = /^[a-z]+-[a-z]+-[a-z]+$/; 32 32 if (!REF_PATTERN.test(opts.ref)) { 33 33 console.error('error: --ref must be exactly three lowercase words separated by dashes (e.g. fast-cache-invalidation)'); 34 34 process.exitCode = 1;
+2 -1
src/cmd/skim.js
··· 6 6 import { restoreAgent } from '../lib/oauth.js'; 7 7 import { readProjectConfig } from '../lib/vit-dir.js'; 8 8 import { requireAgent } from '../lib/agent.js'; 9 + import { resolveRef } from '../lib/cap-ref.js'; 9 10 10 11 export default function register(program) { 11 12 program ··· 106 107 const time = rec.value.createdAt || 'unknown'; 107 108 const title = rec.value.title || ''; 108 109 const description = rec.value.description || ''; 109 - const ref = rec.value.ref || ''; 110 + const ref = resolveRef(rec.value, rec.cid); 110 111 const text = rec.value.text || ''; 111 112 console.log(`[${short}] ${time}`); 112 113 if (title || ref) {
+3
src/cmd/vet.js
··· 5 5 import { restoreAgent } from '../lib/oauth.js'; 6 6 import { appendLog } from '../lib/vit-dir.js'; 7 7 import { requireNotAgent } from '../lib/agent.js'; 8 + import { resolveRef } from '../lib/cap-ref.js'; 8 9 9 10 export default function register(program) { 10 11 program ··· 76 77 const time = record.createdAt || 'unknown'; 77 78 const beacon = record.beacon || 'none'; 78 79 const text = record.text || ''; 80 + const ref = resolveRef(record, res.data.cid); 79 81 80 82 console.log('=== Cap Review ==='); 81 83 console.log('Review this cap carefully before trusting it.'); ··· 83 85 console.log(` Author: ${author}`); 84 86 console.log(` Time: ${time}`); 85 87 console.log(` Beacon: ${beacon}`); 88 + console.log(` Ref: ${ref}`); 86 89 console.log(''); 87 90 console.log('--- Text ---'); 88 91 console.log(text);
+23
src/lib/cap-ref.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { sha256 } from '@noble/hashes/sha256'; 5 + import { wordlist } from '@scure/bip39/wordlists/english.js'; 6 + 7 + export const REF_PATTERN = /^[a-z]+-[a-z]+-[a-z]+$/; 8 + 9 + export function hashTo3Words(input) { 10 + const hash = sha256(new TextEncoder().encode(input)); 11 + // Take first 33 bits -> 3 x 11-bit indices into BIP39 wordlist (2048 words) 12 + const i0 = (hash[0] << 3) | (hash[1] >> 5); 13 + const i1 = ((hash[1] & 0x1f) << 6) | (hash[2] >> 2); 14 + const i2 = ((hash[2] & 0x03) << 9) | (hash[3] << 1) | (hash[4] >> 7); 15 + return `${wordlist[i0]}-${wordlist[i1]}-${wordlist[i2]}`; 16 + } 17 + 18 + export function resolveRef(record, cid) { 19 + if (record?.ref && REF_PATTERN.test(record.ref)) { 20 + return record.ref; 21 + } 22 + return hashTo3Words(cid); 23 + }
+57
test/cap-ref.test.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import { describe, test, expect } from 'bun:test'; 5 + import { hashTo3Words, resolveRef, REF_PATTERN } from '../src/lib/cap-ref.js'; 6 + 7 + describe('hashTo3Words', () => { 8 + test('returns consistent output for same input', () => { 9 + const a = hashTo3Words('bafyreib1234567890abcdef'); 10 + const b = hashTo3Words('bafyreib1234567890abcdef'); 11 + expect(a).toBe(b); 12 + }); 13 + 14 + test('output matches REF_PATTERN', () => { 15 + const ref = hashTo3Words('bafyreib1234567890abcdef'); 16 + expect(REF_PATTERN.test(ref)).toBe(true); 17 + }); 18 + 19 + test('different inputs produce different outputs', () => { 20 + const a = hashTo3Words('cid-one'); 21 + const b = hashTo3Words('cid-two'); 22 + expect(a).not.toBe(b); 23 + }); 24 + 25 + test('uses full 11 bits for first word index', () => { 26 + expect(hashTo3Words('probe-0')).toBe('risk-furnace-affair'); 27 + }); 28 + }); 29 + 30 + describe('resolveRef', () => { 31 + test('returns record.ref when valid', () => { 32 + const ref = resolveRef({ ref: 'fast-cache-invalidation' }, 'some-cid'); 33 + expect(ref).toBe('fast-cache-invalidation'); 34 + }); 35 + 36 + test('returns derived ref when record.ref is missing', () => { 37 + const ref = resolveRef({}, 'some-cid'); 38 + expect(REF_PATTERN.test(ref)).toBe(true); 39 + expect(ref).toBe(hashTo3Words('some-cid')); 40 + }); 41 + 42 + test('returns derived ref when record.ref is empty string', () => { 43 + const ref = resolveRef({ ref: '' }, 'some-cid'); 44 + expect(REF_PATTERN.test(ref)).toBe(true); 45 + }); 46 + 47 + test('returns derived ref when record.ref is malformed', () => { 48 + const ref = resolveRef({ ref: 'Bad-Format' }, 'some-cid'); 49 + expect(REF_PATTERN.test(ref)).toBe(true); 50 + expect(ref).toBe(hashTo3Words('some-cid')); 51 + }); 52 + 53 + test('returns derived ref when record is null', () => { 54 + const ref = resolveRef(null, 'some-cid'); 55 + expect(REF_PATTERN.test(ref)).toBe(true); 56 + }); 57 + });