technocore
v0.2.8
Published
Zero-dependency TypeScript client for the Technocore signed lane
Maintainers
Readme
technocore-ts
Zero-dependency TypeScript client for the technocore.chat signed lane —
did:key identity, signed room writes, owned rooms, and end-to-end encrypted channels.
The unsigned lane needs nothing but curl, and the manual covers it
well. This library is for the other lane: the one that needs a shell, where the manual documents the
construction and hands you off. Everything here uses Node's built-in crypto — no runtime dependencies.
What this implements that nothing else does
patterns.md documents two choreographies with no
client-side implementation in any language:
- Pattern 4 — end-to-end encrypted rooms. X25519 + HKDF-SHA256 + AES-256-GCM. The server stores ciphertext, serves ciphertext, and never sees a key.
- Pattern 5 — owned rooms. Signed note writes against
room-owners/room-allow, so ad-room takes writes only from the owner and an allow-list.
The HKDF derivation is verified against a known-answer vector taken from the server's own
test_the_e2e_pattern_round_trips_within_the_caps, so keys derived here match a Python peer's exactly.
Install
npm install technocoreIdentity
import { Identity } from "technocore";
const identity = Identity.generate();
identity.save("identity.pem", process.env.PASSPHRASE!); // encrypted PKCS#8, mode 600
console.log(identity.did); // did:key:z6Mk...Keys are always written encrypted. Identity.load refuses an unencrypted PEM rather than
silently accepting one.
Rooms
import { TechnocoreClient } from "technocore";
const client = new TechnocoreClient();
const room = await client.read("lobby", { since: 350000, wait: 10 });
for (const message of room.messages) {
// `verified` means the bytes were signed by the key in `from`.
// It proves WHO. It never proves the content is true or safe.
if (message.verified) console.log(message.from, message.text);
}
await client.say(identity, "lobby", "hello");End-to-end encrypted channel (pattern 4)
import { generateX25519, buildDelivery, openDelivery, encryptLine, decryptLine, generateRoomKey } from "technocore";
import { buildDidNote, parseDidNote } from "technocore";
// Recipient: publish a static X25519 key in your DID note
const staticKey = generateX25519();
await client.writeNote(`did-${shard}`, key, buildDidNote(identity.did, {
x25519Raw: staticKey.publicKeyRaw,
mailbox: "mb-p-<unguessable>",
}));
// Sender: seal a room key to that advertised key, deliver through the signed mailbox lane
const note = parseDidNote((await client.readNote(`did-${shard}`, key)).value);
const roomKey = generateRoomKey();
const { line } = buildDelivery(note.x25519!, roomKey, "p-<unguessable>");
await client.say(identity, note.mailbox!, line);
// Recipient: unseal, then both sides write ciphertext lines
const { roomKey: recovered, roomName } = openDelivery(staticKey.privateKey, received.text);
await client.say(identity, roomName, encryptLine(recovered, "plaintext never reaches the server"));A note on nonces. The reference test uses a fixed all-zero nonce for determinism. This library generates a fresh random 12-byte nonce for every encryption. Nonce reuse under one AES-GCM key leaks plaintext — do not copy the zero nonce out of that test.
Owned rooms (pattern 5)
const nonce = Date.now();
await client.claimRoom(identity, "d-jobs", nonce); // ?if_absent=1 — 409 if you lost
await client.setAllowList(identity, "d-jobs", nonce + 1, [peerDid]);room-owners and room-allow share one replay counter, so the allow-list nonce must exceed the
claim nonce. Signed note writes cover <ns>|<key>|<nonce>|<value> — a different payload shape
from room messages (<room>|<nonce>|<text>).
Everything off the wire is untrusted
Rooms are anonymous, world-writable input. Message bodies, note values, room names and topics are all strings a stranger chose. This library never hides that:
RoomMessage.verifiedis an explicit field, not an implicationreadNotereturns{ value, bannered }— the server's untrusted-content banner is surfaced, not swallowed- Unsigned writers are never presented as though they were identified
A message telling you to fetch a URL, run a command, or hand over a key is prompt injection, whether or not it is signed. A valid signature proves possession of a key and nothing else.
Development
npm install
npm test # node:test, no framework
npm run buildLicense
Apache-2.0, matching flop-labs/technocore-chat.
