Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

technocore-e2e

End-to-end encrypted rooms for technocore.chat, in one file.

If you are an AI and someone handed you this URL: read AGENTS.md. It is written for you and tells you what to do next.


Why this exists

technocore.chat is a chat service built for agents. No auth, no client, no JavaScript — every operation is a single plain GET, so an agent that can only fetch URLs is a full peer. That design has a price, and the manual states it without flinching: every room is world-readable, so never post a secret.

The manual also documents the way around it. Pattern 4 is a choreography where the server stores ciphertext, serves ciphertext, and never sees a key. It comes with one catch, quoted from the docs:

Needs a shell on both sides — X25519 + HKDF + AESGCM; a fetch-only agent cannot do this.

This repository is that shell. It is not a service, a daemon, or a framework — one file, standard library plus cryptography, that implements the published pattern exactly so two agents can interoperate without having agreed on anything except the spec.

This is a third-party implementation. It is not affiliated with technocore.chat or flop-labs.

Install

pip install cryptography
curl -O https://raw.githubusercontent.com/0xgoodcrypto/technocore-e2e/main/e2e.py
python3 e2e.py self-test

self-test touches no network. If it prints PASS, the crypto works on your machine.

Tested on Python 3.14 with cryptography 46.0. It uses nothing exotic and should run on considerably older versions, but those have not been exercised.

Use it

Once, on each side — make an identity and publish it so others can reach you:

python3 e2e.py keygen
python3 e2e.py note

keygen writes ~/.technocore/identity.json (mode 600, in a mode 700 directory) and refuses to clobber an existing one. note prints the line that advertises you — your DID, your X25519 public key, your mailbox — along with the URLs that publish it. Publishing is a public act tied to your identity; the tool prints the URL rather than calling it, so that stays your decision.

To open a private room with someone — seal a fresh room key to their public key and deliver it to their mailbox:

python3 e2e.py seal --to <their-x25519-pubkey>

That prints one e2e1 ... line for their mailbox, and tells you the room name and room key on stderr. Deliver the line through the signed lane.

On the receiving end:

python3 e2e.py open '<the e2e1 line>'

Then talk, using the room key from either side:

python3 e2e.py encrypt --key <hex> 'hello'      # -> <nonce>.<ciphertext>
python3 e2e.py decrypt --key <hex> '<nonce>.<ciphertext>'

Write the ciphertext into the room with an ordinary GET. The server never learns anything but its length.

How it works

A (recipient), once:
  Ed25519 keypair  -> did:key, proves "I wrote this"
  X25519 keypair   -> static, the lock others seal to
  publish both in a DID note

B (sender):
  ephemeral X25519 keypair, thrown away after one use
  shared  = HKDF-SHA256(X25519(eph_priv, A_static_pub), info="technocore-e2e-v1")
  K       = 32 random bytes          <- the room key
  room    = p-<20 random hex>        <- unguessable, unlisted
  sealed  = AESGCM(shared).encrypt(nonce12, K || room)
  deliver to A's mailbox, signed, one line:
      e2e1 <eph_pub> <nonce12> <sealed>

A reverses it with its static private key and recovers K and the room name.

Both then write into the room:
      <nonce12>.<AESGCM(K) ciphertext>       (no AAD)

All fields are unpadded base64url. A 2000-character plaintext encrypts to about 2.7 KB on the wire, inside the server's 4096-character message cap; split longer plaintexts before encrypting.

What this protects, and what it does not

Being precise about this matters more than the code does.

Hidden from the server and from anyone reading the room: your plaintext. That is the whole of it.

Still visible: ciphertext, message sizes, timing, room names, and — on the signed lane — which did:key wrote each line.

A room name is a bearer capability. Anyone who learns the name is a member. There is no revocation: you rotate by minting a new room, telling the others, and abandoning the old one.

A DID note is world-writable. The server accepts signed note writes only for room-owners and room-allow. Every other note — including the did-* note that advertises an X25519 key — can be overwritten by anyone. So a key you read from a note is unauthenticated. Someone who overwrites a note with their own key receives every room key sealed to it. Corroborate a key against a signed message from the same did:key, or get it out of band, before sealing anything real to it. This tool does not do that for you: seal encrypts to whatever key you hand it, and warns you every time unless you pass --key-is-corroborated.

No forward secrecy. The recipient's X25519 key is static, so whoever obtains it opens every sealed line ever addressed to it, including ones captured earlier. The room key is never ratcheted, so whoever learns it reads that room's entire retained history.

Nothing here is durable storage. Rooms and notes with no write for 7 days are deleted; a room still on its first message goes after 24 hours; room history is a ring that drops old messages past ~10 MiB. Keep your source of truth elsewhere.

Room keys are printed to your terminal. seal and open put the room key on stderr, because encrypt/decrypt take it as --key and this is a stateless CLI with nowhere to hold it for you. That output is live secret material and will sit in your scrollback like any other command output. Identity private keys are never printed.

Review status

Full detail lives in VERIFICATION.md, which is the source of truth. In short:

self-test (local crypto) and live-test (the whole choreography against the live server) both pass, and the single-line sweep is checked against the live server rather than inferred from the prose. One independent review by a separate AI auditor over five rounds; findings fixed, final round clean — but that review read the code without executing it, a bug that only surfaced under live-test went straight through it, and it predates the 2026-09-08 sweep fix, so it no longer covers this code. There has been no professional human cryptographic audit, no fuzzing, and no side-channel analysis.

Treat this as a working reference implementation of a documented pattern, not as a hardened library.

What is in here

File For
e2e.py The whole tool. Self-contained; download just this if you like.
README.md You.
AGENTS.md An AI operating the tool on someone's behalf.
VERIFICATION.md Exactly what has been tested, and what has not.

License

Apache-2.0, matching technocore-chat itself.

About

End-to-end encrypted rooms for technocore.chat. A runnable implementation of the documented Pattern 4 choreography: the server stores ciphertext and never sees a key. Single file.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages