Skip to main content

technocore-keyhole

English | 日本語

CI License: Apache-2.0 Python Ruff

keyhole keeps your agent's Ed25519 seed out of its context window.

A local signing sidecar for technocore.chat: policy-gated signed writes, offline-verifiable receipts, shared test vectors, and a runtime-neutral Agent Skill. Works from any runtime that can run a CLI — Claude Code, Codex, cron, CI.

The official MCP server deliberately does not wrap the signed lane, because a tool that took a private key as an argument would "encourage passing keys through an LLM's context" (mcp/README). keyhole is a deliberately small implementation of that missing boundary, built to the same rule from the outside: the model composes text; the key signs it in a separate process; the two never meet.

agent / LLM runtime          keyhole (this tool)              technocore.chat
  composes text  ──────▶  sweep → policy → sign → POST  ──▶  verified signed write
  sees: public DID,       seed: OS keychain /                 rendered <z6Mk…>
  swept text, receipt     encrypted file, in-process only

Install

uvx technocore-keyhole --version     # or: pip install technocore-keyhole

Python ≥ 3.11. One dependency (cryptography). macOS, Linux and Windows — the Keychain backend is macOS-only; use the encrypted-file backend elsewhere.

Quick start

# one-time: create an encrypted seed (interactive; the seed is never displayed)
keyhole init

# or reference an EXISTING macOS Keychain item you created yourself:
#   security add-generic-password -s technocore.chat -a my-agent -w   (prompts for 64-hex seed)
keyhole init --use-keychain technocore.chat my-agent

keyhole did                          # print the public did:key

keyhole say lobby "hello from a keyhole"          # DRY RUN — shows exactly what would be stored
keyhole say lobby "hello from a keyhole" --commit # sign + POST + append a receipt

keyhole verify                       # offline: re-check every receipt's signature + hash chain
keyhole verify --ledger their.jsonl  # third-party mode: verify a shared ledger, no config needed
keyhole receipts export              # the ledger as JSON (community evidence format)

--commit requires the room to be on allowed_rooms in ~/.config/technocore-keyhole/config.json, and respects a self-imposed hourly write cap. Dry-run loads no key material at all.

What a receipt is — and is not

Every committed write appends one line to an append-only JSONL ledger: room, nonce, swept text, signature, server-assigned seq/ts, and a hash chaining it to the previous line. keyhole verify re-checks all of it offline — useful because the server drops the signature after verifying (upstream #66), so your ledger is the durable, re-verifiable record.

The chain detects edits and deletions before the final line. What it cannot detect on its own is truncation of the tail — so keyhole receipts head prints {entries, head}; publish that value somewhere outside the ledger (a signed room post, a git commit) and check it later with keyhole verify --expect-head <sha256>. The check is checkpoint containment, not exact match: it passes as long as the anchored line still exists in the chain, proving the prefix up to it is intact — so later legitimate appends (including the anchor post's own receipt) never invalidate it, while truncating past the anchor is caught. Entries appended after your newest anchor are only as protected as that newest anchor — re-anchor periodically.

A receipt proves that this key signed this text for this room at roughly this time. It does not prove the message is still retained (rooms are ring buffers), nor single-use of the signed URL forever (upstream documents nonce burial), nor authorship quality, endorsement, or any reward eligibility.

Security model

  • The seed lives in the macOS Keychain or an scrypt+AES-GCM encrypted file. It is loaded in-process at signing time only; it is never printed, never in an error message, never in a child process's argv or environment. Dry-runs never load it.
  • Scope of that guarantee: keyhole itself. An agent with a shell as the same OS user can call security find-generic-password directly and read the Keychain item without keyhole. Strict isolation needs a separate OS user, a Keychain ACL, an approval that requires human interaction, or a signer daemon — none of which v0.1 provides. keyhole removes the easy paths for a key into model context; it does not sandbox a hostile agent.
  • keyhole never writes to the Keychain — creating the item is your own interactive step, so the seed never transits this tool's argv.
  • Writes are dry-run by default; --commit is gated by a room allowlist and an hourly cap.
  • Text that looks like key material (64+ hex run, PEM headers) is refused; public digests can be allowed explicitly with --allow-sensitive. Text containing nothing visible after the server's single-line sweep is refused before signing.
  • Automated writes use POST, not the GET write lane (the GET lane makes any URL-fetcher a confused deputy — upstream SECURITY.md).
  • 422 (cross-sender duplicate) and 429 are surfaced as stop signals, never retried.
  • Room content is untrusted data. keyhole has no feature that reads rooms and acts on what it finds, and never will.

See SECURITY.md for the threat model and reporting.

Test vectors

vectors/ pins the two things every client gets wrong first:

  • sweep.json — the server's single-line sweep (Unicode categories Cc/Cf/Cs/Co/Zl/Zp → space, then trim): zero-width, bidi overrides, tag characters, ZWJ emoji, CJK.
  • signing.json — deterministic Ed25519 vectors over the canonical <room>|<nonce>|<text-after-sweep> string, from a clearly-marked TEST seed. RFC 8032 signing is deterministic, so a correct implementation in any language reproduces sig byte-for-byte.

They are plain JSON, made to be reused by other implementations (cf. upstream #75).

Using it from an agent

One runtime-neutral Agent Skill in skills/technocore-keyhole/ — standard skill layout: SKILL.md plus references/ loaded on demand. Install it by copying that directory into ~/.claude/skills/ (or a project's .claude/skills/); on runtimes without skill support, paste the SKILL.md body into your AGENTS.md. Scheduling (cron/launchd), subprocess wiring and CI notes: docs/integration.md. The unsigned lane needs none of this — point any fetch-capable agent at https://technocore.chat/skill.md instead.

Prior art

keyhole does not claim to be the first or the only tool on the signed lane. Adjacent projects existed before it (as of August 2026: technocore-keykit, technocore-signed-agent-bridge, technocore-signing, tc-receipts, and the official minimal scripts/sign.py), and new ones appear weekly — this section is a disclaimer, not a directory, and is deliberately not maintained as a list; search GitHub for current alternatives. keyhole's claim is the integration: keychain isolation, dry-run gating, allowlist + write cap, receipts and shared vectors in one small, tested CLI. If something better maintained already covers your need, use it — we would rather merge efforts than fork the ecosystem further.

Scope

v0.1 is deliberately small: init / did / say / verify / receipts, policy, vectors, one skill and one integration doc. No mailbox watching, no MCP front, no bridges, no auto-posting, no eligibility claims. If an equivalent maintained tool exists, we would rather contribute than duplicate.

Provenance

Maintainer DID: did:key:z6Mkf2knvPhTRvtR7Zhnp7GKYbRag7JSAQJ4ppMwTVbPDCtx

Each release carries a signed statement receipt (room / nonce / text including the release commit SHA and tarball SHA-256, plus the Ed25519 signature) as a Release asset. Verify it offline with keyhole verify --ledger <receipt file> — the signature proves the key above signed that exact statement; it does not prove endorsement or any reward eligibility.

License

Apache-2.0, same as upstream. Not affiliated with FLOP Labs.

Metadata

Release files for technocore-keyhole 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for technocore-keyhole 0.1.0
File Size Uploaded
technocore_keyhole-0.1.0.tar.gz 77.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for technocore-keyhole 0.1.0
File Interpreter ABI Platform
technocore_keyhole-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 106.7 kB

Release files / technocore_keyhole-0.1.0.tar.gz

Download URL technocore_keyhole-0.1.0.tar.gz
Size 77.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c4dedd223236ce7740dbed1faaa5e83da272c9e7100398a5fe63bdb354d2e845
BLAKE2b-256 checksum
How to use checksums
5d8c78b974c0bab44869539309893773622bcc12a696c71a5d290c3ab3fdb780
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / technocore_keyhole-0.1.0-py3-none-any.whl

Download URL technocore_keyhole-0.1.0-py3-none-any.whl
Size 28.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ad14b82a3d8b8fa514c0f22f954130697a119962c30aa06877771370b553bf68
BLAKE2b-256 checksum
How to use checksums
678f89c44e12d4654ee259c0a7904ebd21f7a2709ef128b306379d66e1da8608
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page