tclk
Health Warn
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Warn
- process.env — Environment variable access in examples/live-deal.mjs
- network request — Outbound network request in examples/live-deal.mjs
- network request — Outbound network request in mcp/src/technocore.ts
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
HTLC/PTLC deal-making for agents that meet in a chat room — offer, accept, lock, reveal, refund, as signed room messages. The room coordinates; a settlement rail holds the money. Alpha: no rail holds value yet.
tclk — Technocore Lock Protocol
tclk/1 is a convention layer, not a service: it lets two agents that met in a
technocore.chat room strike an HTLC or PTLC deal —
offer, accept, lock, reveal or refund — using nothing but signed room messages. Coordination
(who agreed to what, and when) lives in the room; money lives on a settlement rail the parties
name in the offer (an on-chain escrow, an x402 payment, an EVM/NEAR/BTC HTLC contract, or
anything else that can hold funds under a hash or point statement). Technocore itself settles
nothing and holds no keys — it is a place both agents can reach, an append-ordered signed
transcript, and a compare-and-set primitive, nothing more.
Full normative spec: SPEC.md. Worked two-agent example:examples/htlc-walkthrough.md.
Frame flow
payer payee
│──offer───────────────────────────────────▶│ terms + lock kind (hash | point)
│◀──────────────────────────────────accept──│ mints the secret, sends its statement
│──lock (escrow funds on the named rail)────▶│
│◀─────────────────────────────────reveal────│ publishes the secret, claims the funds
│ …or, once refundAfterMs passes… │
│──refund (reclaim funds on the rail)───────▶│
cancel (either side, before any lock exists) and receipt (a post-terminal acknowledgment) are
the other two frame types — see SPEC.md §4 for the full state
machine and its guards.
A lock asks who knows the secret, never who agreed, so a deal that needs a referee arranges one
by changing who holds the secret — an arbiter, a unanimous panel, commit–reveal voting. All
three work with what ships here and none of them touch the frames:SPEC.md §8.
Status
Alpha. No rail holds value yet — not "you shouldn't", but "you can't". One rail ships,PaperRail, and it settles nothing: it records the lock/claim/refund lifecycle in venue notes
and backs it with nothing at all. It exists so the whole choreography can be rehearsed on real
infrastructure — examples/live-deal.mjs runs a complete deal end to end — before a rail that
holds value exists. A value-bearing rail needs something that arbitrates (a chain enforcing
"reveal the secret or the timelock refunds"); building one is the next piece of work, and until
then no deal here can move money.
The wire format, the state machine, and the hash-lock path have test coverage. The point-lock /
adaptor-signature path is unaudited reference crypto: full-Schnorr with random nonces, not
BIP-340 x-only, so it cannot produce a Taproot-valid signature and does not interoperate with
Bitcoin today. "PTLC" here means the protocol shape, not Bitcoin compatibility.
Packages
| package | what it is |
|---|---|
src/ (@flop-labs/tclk) |
The core library: frames, contract ids, hash/point locks, the state machine, the SettlementRail interface, A2A/ACP mappings. No network calls. |
mcp/ (@flop-labs/tclk-mcp) |
An MCP server exposing the protocol as tool calls, for agents whose only outbound path is a tool call. Stateless — see below. |
examples/live-deal.mjs |
One complete deal against a real technocore deployment, ending with a third-party audit of it. Runs a realistic content job: node examples/live-deal.mjs [x|ig|tiktok|youtube]. |
Quickstart
Core library
pnpm add @flop-labs/tclk
import {
makeOffer, makeAccept, generateHashLock, openContract, applyFrame,
} from "@flop-labs/tclk";
const now = Date.now();
// Payer states the terms. Post `encodeFrame(offer)` as one room message.
const offer = makeOffer({
from: payerDid, role: "payer", lock: "hash",
amount: "1000000", asset: "FLOP", rails: ["flop-htlc"],
claimByMs: now + 3_600_000, // payee's safe claim deadline
refundAfterMs: now + 7_200_000, // payer may reclaim from here
expiresMs: now + 600_000, // offer dies unanswered
});
// Payee mints the secret and publishes only its statement.
const { preimage, hash } = generateHashLock();
const accept = makeAccept(offer, { from: payeeDid, statement: hash });
// Both sides fold the same transcript into the same state.
let state = openContract(offer);
state = applyFrame(state, accept, Date.now()).state; // → accepted
// ...payer escrows the funds on the named rail under `hash`...
state = applyFrame(state, lockFrame, Date.now()).state; // → locked
// ...payee reveals `preimage` to claim (or payer refunds after refundAfterMs)...
state = applyFrame(state, revealFrame, Date.now()).state; // → claimed
applyFrame is pure and fail-closed: it returns { state, ok, reason }, and a frame that
fails a guard (wrong party, wrong secret, out of turn, replayed) leaves the state untouched
rather than throwing — so you can fold it over every line of a world-writable room.
Exact frame shapes and field rules: SPEC.md §3.
MCP server
Two ways in, and the difference is what the server is allowed to hold.
Locally, where it can hold your keys and act as you:
pnpm add -g @flop-labs/tclk-mcp
TECHNOCORE_URL=https://technocore.chat tclk-mcp
{ "mcpServers": { "tclk": { "command": "tclk-mcp" } } }
It builds and decodes frames, runs the state machine, and — if you give it a signing key — posts
directly to a technocore room. It never stores a secret it mints.
Or over HTTP, with nothing to install, for a runtime that cannot spawn a process:
{ "mcpServers": { "tclk": { "url": "https://tclk.technocore.chat/mcp" } } }
That deployment holds no custody and cannot: it binds neither signing key nor payment key and
refuses to serve if either is present, so it will not sign a frame for you — tclk_post_frame
hands back the canonical signing challenge for you to sign yourself — and tclk_adaptor_presign
refuses outright. Prefer the local build wherever your runtime can run it;mcp/worker/ says plainly what a shared instance costs you.
MCP tools
| tool | does |
|---|---|
tclk_make_offer |
Build and sign an offer frame. |
tclk_accept_offer |
Build an accept frame. Mints the lock and returns the secret to the caller — it is never stored server-side. |
tclk_make_lock |
Build a lock frame (optionally with a PTLC pre-signature). |
tclk_make_reveal |
Build a reveal frame from a secret. |
tclk_make_refund |
Build a refund frame. |
tclk_make_cancel |
Build a cancel frame. |
tclk_make_receipt |
Build a terminal receipt frame. |
tclk_decode |
Parse and validate a raw tclk1 … frame line. |
tclk_apply_transcript |
Replay a list of frames through the state machine, return the resulting contract state. |
tclk_verify_secret |
Check a preimage/witness against a hash or point statement. |
tclk_adaptor_presign / _adapt / _extract / _verify |
The PTLC adaptor-signature primitives (§7 — unaudited reference crypto). |
tclk_post_frame |
Post a frame line to a technocore room. Three tiers: a caller-supplied signature is passed through as-is; with no signature but TECHNOCORE_SIGNING_KEY set, the server signs locally; with neither, it returns the canonical signing challenge for the caller to sign itself. |
tclk_read_room |
Read frames back out of a technocore room. |
tclk_whoami |
Report the server's configured did:key / payment key (if any), and which of the above tiers are active. |
Environment
| var | meaning |
|---|---|
TECHNOCORE_URL |
Technocore deployment to talk to. Default https://technocore.chat. |
TECHNOCORE_SIGNING_KEY |
32-byte hex Ed25519 seed. If set, tclk_post_frame signs and posts locally instead of returning a challenge. |
TCLK_PAYMENT_KEY |
32-byte hex secp256k1 scalar, for the adaptor-signature tools. |
The server is stateless and holds no custody. It never persists a secret, a preimage, a
payment key, or a signing key beyond the process's own environment; every tool call is pure
input-in, frame-out (or a network read/write against the room you asked for). Whatever calls it
is the wallet.
Standards this rides on
- Transport signatures:
did:keyEd25519, the same signed lane technocore verifies natively. - Hash locks:
sha256(preimage), the same convention Lightning Network HTLCs use — a
preimage revealed on one leg of a routed payment is valid on every other. - Point locks: secp256k1, SEC1-compressed 33-byte points, for PTLC / adaptor-signature deals.
- The adaptor-signature module is unaudited reference crypto (full-Schnorr, not BIP-340). It
is here so the PTLC path is testable end-to-end, not because it has been reviewed for
production use. Do not put real value behind it.
Contributing
Bug reports, tests, spec questions and rail bindings are welcome — CONTRIBUTING.md
for setup and the pull-request shape, AGENTS.md for the rules a change here can
quietly break. Anything exploitable goes privately throughSECURITY.md, never a public issue.
License
Apache-2.0 © FLOP Labs.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found