Skip to content

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 17:47
d472152

Added

  • Spec (D-82, owner decision): 04 §2.3 now defines the owner seed blobs. ALKP v1 is the passkey PRF wrap (owner key and credential ID in the header, random nonce, whole header as AAD, size and owner checks); ALKR v1 is the recovery-code wrap (the ALKO layout with its own magic and label, generated codes only, Argon2id 64 MiB / 3 / 1). M1-1k is unblocked: vectors first, then code.
  • Security utilities for files (M1-15, S1, 04 §4, §7; not wired into any tool yet, M1-7f and M1-9d use them): @agentlink/core gets the pure checks (checkFetchUrl on the raw URL string: https only, no numeric host notation in any base, no local or internal names, no user info; pickPublicAddress: every resolved address must be globally routable; decidePath for both POSIX and Windows paths; sanitizeFileName). @agentlink/node gets nodeSafeFetch (one lookup per hop, connect to the checked address, at most 2 redirects each re-checked, byte cap, one deadline, no proxy; errors never carry the URL), resolveAllowedFile / openAllowedFile (realpath on both sides; hidden files, ~/.ssh, ~/.agentlink and keychain folders always refused, also through links) and createInboxFile (sanitised name, exclusive create, never through a link). In Workers (G-14, proposed) the raw URL check applies and the address check relies on Workers egress; that path lands with M2-3b.
  • Crypto (M1-1j, S2): EIP-712 typed data and hashing for the OCC v2 proof types Record, Receipt and Registration under the chain domain {name: "OCC", version: "2", chainId, verifyingContract}, with strict 05 §3.1 JSON-form parsing (refused, never normalised); recordStructHash; the recipient commitment keccak256(abi.encode(recipientAgentEvm, salt)); and EIP-3009 ReceiveWithAuthorization typed data under a token domain the caller reads from chain. The eip712-proof vector suite is no longer pending. The revoke struct waits for G-20 (M1-1i-b).
  • Storage (M1-6a, S2): @agentlink/storage has the StorageAdapter interface (07 §5.1), the inline adapter (ciphertext up to 12 288 bytes, base64url in the payload) and the S3-compatible adapter over aws4fetch: upload, a presigned download link clamped to 7 days and to the expiry of temporary credentials, and delete. Objects are keyed agentlink/<storagePrefix>/<random> with no metadata; credentials come from an injected provider; no credential, link or object key is logged. Not yet wired into the tools; integration tests against MinIO and R2 follow in M1-6a-2.
  • Local approval server (M1-8d, S2, @agentlink/node; not wired into the CLI yet, M1-11a): an HTTP server on 127.0.0.1 and a random port with a view-only session token kept in memory (exchanged for an HttpOnly cookie, never written to disk or logged), exact Host and Origin checks against DNS rebinding, a CSRF token and non-simple requests for decisions, and owner presence (owner code or owner passphrase) for every approve or deny, signed as one approval.decide that core verifies. Each approval ID is decided once through it; a fixed OS notification and the /approve/<id> route are included. The same-user script test runs against the live server.
  • Live relay subscriptions (M1-9s): while agentlink mcp runs and holds the publisher lease, new messages and handshakes are stored and processed as they reach a relay instead of on the next 30 s poll. The core Transport gains subscribe and the client subscribeInbox (same receive path as syncInbox: dedup by gift-wrap ID and envelopeHash, one store transaction, rate limit). The background loop keeps a catch-up every 5 min as a fallback, polls when no subscription can open, and closes the subscription on lease loss and on shutdown.
  • Spec text sync (M1-9s): the 07 §4.1 interface sketch now shows subscribe with the implemented sync handler shape (relays, identities, persistence, onEnvelope) and an unsubscribe that resolves once in-flight handlers settle; no protocol change.
  • Dogfood tooling (M1-23-b): tools/dogfood/run.mjs --role a|b runs the pairing, messages, owner approvals and the J8 negotiation across two Macs, one agent per Mac, with the invite link and run ID exchanged by hand; --same-owner for one person at both Macs. Single-machine mode is unchanged. Runbook RB-17 section 10 has the commands.
  • Test vectors (M1-1i, S2): two new suites, pending until their code lands. eip712-proof (M1-1j) covers EIP-712 hashing of Record, Receipt and Registration under the OCC v2 chain domain, the recipient commitment, and the Record/Receipt JSON-form rejections. prf-wrap (M1-1k) covers the passkey PRF salt and wrap key (04 §2.3). STREAM and revoke vectors wait for G-26 and G-20 (new card M1-1i-b).
  • CLI (M1-9w, S1): agentlink init --harness claude-desktop sets up the Claude desktop app (macOS and Windows; its own claude_desktop_config.json, not Claude Code's file), with the usual diff, confirmation, backup and refusal of malformed files. On Linux it says the app isn't available there.
  • Docs (DOCS-SETUP, S1): a beginner page, "Set up AgentLink in your AI tool", with the Node check, one install line per tool, the owner step, a first test, pairing two of your own tools and the top troubleshooting fixes. Claude Desktop is marked as merged but not in v0.1.0.
  • Owner approvals (M1-11a, S2, 08 §1, 11 §6.2.1, 04 §2.5.2, D-68): the local approval page on http://localhost:<port> lists your waiting approvals as cards (who, what, cost, expiry, the rule that asked, and what your AI chose), soonest to expire first. Mark each Approve, Deny or Later, then confirm exactly that list with one passkey gesture or one entry of your owner code; terms are listed but always approved on their own card. Before asking, the page checks the list again and tells you what it left out. Text from your AI or a contact is shown as plain text only. The first-run screen explains what approvals need and offers the passkey, or says when this browser has none and the owner code is the way. Each link the agent opens works once (a link in your browser history no longer opens the page), and after 3 wrong owner codes or passkeys in a row the page makes you wait before the next try (5 seconds, doubling up to 5 minutes); agentlink approve in your own terminal is not affected by that wait. "Remember for this contact" and opening the page from the CLI come later.
  • File encryption (M1-1d, S2, 04 §4, D-80): the crypto package encrypts and decrypts files in 1 MiB chunks as a stream, so a large file never has to be in memory whole, and refuses a file whose header was changed or whose chunks were dropped, reordered, repeated or added. The stream test vectors now run in every runtime.
  • Test vectors (M1-1i-b, S2): two new suites, pending until their code lands. stream (M1-1d) covers ALF1 file encryption with the D-80 chunk AAD header ‖ isFinal (04 §4): empty, 1-byte, one-chunk, chunk + 1, multi-chunk, exact-multiple and inline-cap files, and rejection of tampered headers, reordered, dropped, truncated or appended chunks, a flipped final flag and a wrong nonce. eip712-revocation (M1-1j) covers EIP-712 hashing of the D-81 Revocation struct (05 §4), a Registration and a Revocation sharing one owner nonce, and the Revocation JSON-form rejections.
  • Revocation signing data (M1-1j, S2, 05 §4, D-81): the crypto package builds and hashes the EIP-712 Revocation an owner signs to revoke an agent, under the OCC v2 chain domain with the Registry as verifying contract, and refuses every non-canonical form. The eip712-revocation test vectors now run in every runtime.
  • Crypto (M1-1k, S2): @agentlink/crypto seals and opens the owner seed as ALKP v1 under a passkey PRF output and as ALKR v1 under a generated recovery code (24 base32 characters in six groups of four), with header, size and owner checks, one generic error and no partial output on failure. The prf-wrap vector suite (80 cases, including cross-type cases with ALKO and ALKS) is no longer pending.
  • Hands-free two-Mac dogfood: node tools/dogfood/run.mjs --two-mac --remote <ssh-host> runs both Macs from Mac A over ssh, syncs the steps by itself and ends with one combined table. The invite link goes over ssh stdin only; owner approvals are still typed by you in the same terminal (M1-23-d, S1).
  • Auto-sync two-Mac dogfood for macOS: run.mjs --role b --auto-sync on Mac B, then --role a --auto-sync --remote <host> on Mac A. Each agent runs in its own Mac terminal (ssh only moves small files), so the keychain works; approvals are typed locally on each Mac. --two-mac now detects the macOS keychain-over-ssh failure and points to it (M1-23-e, S1).
  • Encrypted drop (M1-27a, S2, D-48): the drop Worker in apps/drop, tested locally under Miniflare and not deployed (deployment is M1-27b; the R2 buckets and lifecycle rule are the owner's M1-28). Signed uploads with no accounts, single-use upload targets that take exactly the declared size (multipart above 64 MiB), a commit that hashes the stored bytes and deletes them on a mismatch, signed downloads until expiry with a download limit of 16, DELETE by the uploading drop key only, an abuse report that deletes only that object, per-key quotas, per-IP (IPv6 per /64) and global limits, and no logs: aggregate counters only. @agentlink/crypto gains the drop request signature (verification and the unsigned event), the drop URL MAC and an incremental SHA-256, with the drop-auth vectors in all runtimes. Spec (07 §5.4, 04 §4, 08 §7; proposed, owner confirms at merge): the signature format, the signed URLs, the limits with the shared-egress and global-cap rules, the download limit, and the CORS policy for browser downloads.
  • Encrypted drop deploy pipeline (M1-27b, S2, D-48): apps/drop/wrangler.toml (staging on workers.dev, production on drop.agentlink.4thtech.io; bucket and Durable Object bindings, a raised CPU limit, and observability, Logpush and Tail Workers off) and .github/workflows/deploy-drop.yml: the bundle is built once, deployed to staging and smoke-tested there (signed upload, download and delete; an expired download refused), then deployed to production automatically once staging is green, with a health check, automatic rollback, a read-back of the 7-day bucket lifecycle rule and of the logging settings, a freeze on failure, and a staging rollback rehearsal. It stays inactive until the owner sets the Cloudflare tokens and the Worker secrets. Process: release-and-deployment.md §1.1, §2, §4.5.1, §6, §8 and ci-cd.md; the G-40 assumption is recorded in §4.5.1.
  • The storage adapter is now tested against a real S3 server on every pull request (a MinIO container over TLS) and every day against a Cloudflare R2 test bucket (M1-6a-2, S1, sensitive path): upload, a link for the recipient capped at 7 days, download through it, expiry and delete, with a check that no link, key or credential is ever logged.
  • Large files can be uploaded to your own S3-compatible storage in parts (M1-6b part 1, S2, 07 §5.2): an interrupted or failed upload is always cancelled, so nothing half-uploaded is left in your bucket, and leftovers from a crash can be cleaned up. A storage safety net can also be set: a bucket rule that deletes AgentLink files after 30 days and cancels any AgentLink upload left unfinished for a day, added next to any rules you already have, never replacing them. The recommended storage key now also includes the two permissions needed to cancel and clean up unfinished uploads (07 §5.2). Encrypting large files on the fly and using this for sending files follow in part 2.
  • Large files are encrypted on the fly as they upload to your own S3-compatible storage, and decrypted on the fly as they download, so a large file never has to fit in memory (M1-6b part 2, S2, 07 §5.2, 04 §4): every file gets its own fresh key; a download stops as soon as it is larger than the file it claims to be; and a downloaded file appears only once all of it has been checked, so a damaged or tampered download never leaves a partial file behind. Using this for sending and receiving files comes with the storage selection and file tools.
  • Owner approvals (M1-8e, S2, 04 §2.3.1, 08 §1): the local approval page on http://localhost:<port> can set up a passkey (Touch ID, Face ID or Windows Hello) with one entry of your owner code, and then approve or deny with that passkey instead of typing the code. Your owner code keeps working; a passkey without the PRF feature is not stored. The spec now names where the passkey copy of the owner key lives (owner-passkey, one per computer user for now) and the checks on every passkey answer (proposed, owner confirms at merge). The page is not opened by the CLI yet.
  • CLI (M1-9z, S2, 04 §2.8): agentlink owner change-code † replaces an exposed owner code with a new generated one. It asks for the current owner code (three tries), shows the new code once and asks for its last four characters, then re-wraps the same owner key: the owner fingerprint and every agent card stay valid. The new wrap is staged and checked before the saved owner key is replaced, so a failure leaves the current code working. Refused in a terminal an AI app controls; a recovery kit is not changed.
  • Owner commands agentlink contacts rename <contactId> <name> and agentlink contacts remove <contactId> (M1-9za, S2, 04 §2.5.1, 06 §8): fix two contacts that show the same name (AMBIGUOUS_CONTACT) by giving one your own name or removing the one you no longer need. Both need your owner code in a terminal you opened; your AI can't do either, and no AI tool was added. agentlink contacts now lists each contact's ID. Spec: new owner-action ops contact.rename and contact.remove (proposed, owner confirms at merge).
  • A nightly two-machine test (OPS-CI-7, S2, process): two real agents on a Mac and a Linux runner pair, message, resist a hostile note and agree terms over the default relays. It is not part of the merge gate, runs only when main has changed, and uses the cheapest model.

Changed

  • Spec (G-26, G-20, S2, D-80, D-81): ALF1 file encryption now binds the 17-byte header into every chunk's AAD (header ‖ u8(isFinal), 04 §4), so a changed header fails decryption. OCCRegistry gets an owner-signed EIP-712 Revocation struct with single-use owner nonces shared with Registration, an epoch check, and revoke while paused (05 §4). Spec only; no code change.

  • CI (OPS-CI-3, S2, process): pr-hygiene accepts task IDs of the form OPS-<WORD>-<n> (for example OPS-CI-3) in PR titles, and the changesets check no longer asks for an empty changeset when a PR touches only the private tooling packages (tools/, e2e/); any other workspace package still requires one.

  • CLI (M1-22-b, S1): agentlink_inbox offers carry a termsParties field naming the agents in their terms as "you", a contact's name (06 §2.1 rule) or null, so summaries need no raw addresses; with openOffers: true, identical offers from one contact (same terms hash) add one hint line (" sent the same offer N times; reply once."). The offers themselves are unchanged. Spec: 06 §2.2 documents termsParties.

  • Core (M1-7i, S2): the policy engine applies the remaining 06 §3 rules. Files: a send needs an owner-allowed folder (none by default, so the tool asks for setup), stays within maxSendSizeMB, and goes inline, to the encrypted drop or to own storage per files.storage (D-48); a received file stays within maxReceiveSizeMB. Proofs: notarize, perActionMaxUSD and dailyBudgetUSD over a rolling 24 h window (G-7), with per-contact rules (G-32) in the 06 §3 steps. The budget check and the reservation of the maximum fee run in one store transaction, a reservation is released only after its deadline with no record on-chain, and a missing, inconsistent or clock-skewed ledger makes every paid proof ask the owner until the next owner-signed policy. Under an invalid policy, sending a file is refused (no folder is allowed) and fetching one needs the owner.

  • CI (M1-9c-c, S2, process): new bundle-check job in ci-gate. It builds the CLI on a runner with no keychain and no native-module build and fails when the published dist/agentlink.js imports a crypto-capable MCP SDK transitive package (jose, pkce-challenge, cookie-signature, etag, hono), an SDK subpath outside the stdio set or an undeclared package, or when src/tools/ bundled for a neutral (Worker) platform reaches a Node built-in, @agentlink/node or the MCP SDK (IR-M1-9c-1 F07, D-76, 06 §1). No new dependency: it reuses the repository's Vite. docs/process/ci-cd.md records it.

  • Spec (M1-9v, S1, 06 §4): the untrusted-content wrapper spec now says that every opening and closing untrusted_content tag inside content is escaped, look-alike forms included, as the code already does. No behaviour change.

  • agentlink_propose_terms and agentlink_respond_to_terms now pass clientRequestId to the core (M1-32-c, S1): a repeated call returns the original result and sends nothing more; a different request under the same key is IDEMPOTENCY_KEY_REUSED, a concurrent repeat is IN_PROGRESS (as agentlink_send), and a repeat whose approval has since been decided reports that approval's state.

  • CLI: an agent that has no card from its owner yet is now told so. agentlink_status, the other tools and agentlink doctor say to run agentlink owner add-agent --agent <name> in your own terminal instead of suggesting init (M1-9u). owner add-agent also explains the case where an owner exists but no agent here is attached to it.

  • OPS-REL-3: the "Version packages" PR now rolls CHANGELOG.md [Unreleased] into the version section (tools/release/roll-changelog.mjs), and a GitHub Release body over 125,000 characters falls back to a short note with a link.

  • CI (OPS-CI-4, S2, process): new sync-pin job in security.yml (push to main, weekly) fails when the secret-scan stub on the sync branch is pinned to a commit older than the last change to secret-scan.yml or .gitleaks.toml on main; sync-protocol.md and ci-cd.md document the re-pin procedure.

  • Local approval page on http://localhost:<port> (M1-31, S2, D-79; 08 §1, 04 §2.5, 06 §2.5, 11 §6.2.1; owner decision 2026-10-17): WebAuthn does not accept an IP address as an RP ID, so the page moves from 127.0.0.1 to localhost for the passkey (M1-8e). The server listens on 127.0.0.1 and ::1 (IPv6-only) on the same random port and nowhere else; if another program holds ::1 on that port it tries a new one (at most 5), and a machine with no IPv6 loopback gets 127.0.0.1 only. Only Host: localhost:<port> and Origin: http://localhost:<port> are accepted; 127.0.0.1:<port> is now refused. The OAuth loopback redirect stays on 127.0.0.1.

  • Process (owner decision, 2026-10-17): open question G-14 (agentlink_fetch_file and SSRF in Workers) blocks only M2-3b, not M1-15; the Workers egress part stays open.

  • Docs: README, knowledge/, llms.txt, docs-site/ and docs/status.yaml now say v0.1.0 is released (16 Oct 2026, npx -y @4thtech-io/agentlink init); the OpenCode and Antigravity quickstart, the messages, contacts, approvals, first-message and troubleshooting pages are filled in. Files, proof and the apps stay planned.

  • Docs (DOCS-DELIVERY, S1): a plain-language page, "How messages are delivered", on live and mailbox delivery, the status-first habit and the limits (relay retention, look-back); live delivery is marked as shipping in the next release.

  • Docs (DOCS-KNOW, S1): knowledge/ is consolidated from eleven numbered files into agentlink-overview.md, agentlink-userguide.md, agentlink-security-and-privacy.md and agentlink-technical.md, following the pattern of The Curator's llm-docs/; knowledge/README.md states the maintenance rule (update on every valuable or user-visible change). llms.txt, the generated status target, the link check and the docs-pass steps follow. No feature is newly claimed as available.

  • Docs updated for M1b progress (DOCS, S0): the README Status and Roadmap, the docs-site status page, the knowledge base and the backlog card statuses now show M1b in progress and separate what is merged from what is released (npm latest is still 0.1.0); the Claude Desktop and Node.js 22.14 notes say which release they ship in.

  • Spec gap G-10 resolved (D-83, S1): the contracts ABI package @4thtech-io/occ-v2-abi is a private in-repo package bundled into the CLI and is not published to npm; card M1-3f updated.

  • Spec (M1-1d, 04 §4): a file key is 32 fresh random bytes per file and is never reused for another file.

  • Spec (M1-1i-b, proposed, owner confirms at merge): 04 §4 fixes the ALF1 version byte (0x01), the chunking at chunk boundaries (a full final chunk; an empty one only for an empty file) and the chunk size (senders 1 MiB; recipients accept 1 B to 16 MiB); 05 §4 and 04 §2.4.1 define the Revocation JSON form.

  • A streaming download may decrypt into a private hidden temporary file and move it into place only once the whole file and both of its fingerprints check out; it is removed on any failure and after a crash (D-84, 04 §4, M1-6b part 2).

  • Owner setup (M1-8h-b, S2, @agentlink/node; IR-M1-8h-1 Lows): owner init asks for the agent's name for contacts before the owner code is shown (Enter keeps the local agent name; up to 32 letters, digits, spaces, dots, apostrophes or hyphens in one script, accepted only if the contact-name rule shows it unchanged, so contacts see it as self-declared). If the first card fails, the rollback removes the owner key only while it is still the one this run stored, so a concurrent run's owner key is never deleted. The generic owner operation no longer signs agent cards at all; cards are signed only in the bound sessions of owner init and owner add-agent.

  • The approval for connecting with a new contact now names who is inviting: their self-declared name (clearly labelled) and their owner fingerprint, so you can tell who you are connecting to (M1-9zb, S1, 06 §2.1).

  • The deterministic e2e suite is now part of the merge gate (OPS-CI-6, S1, process): ci.yml calls it and ci-gate requires it, in pull requests and in the merge queue; docs-only changes skip it.

  • Changelog fragments (OPS-REL-4, S2, process): a PR now adds changelog.d/<card-id>.md instead of a line under CHANGELOG.md [Unreleased], so parallel PRs stop conflicting in the merge queue. The release roll (tools/release/roll-changelog.mjs) folds the fragments and any legacy [Unreleased] lines into the version section and deletes them; a new CI step fails a user-visible, spec or process PR with no entry and warns on a direct [Unreleased] edit. Format in changelog.d/README.md.

Fixed

  • CLI (M1-9w, S1): Gemini CLI is detected by the gemini program or ~/.gemini/settings.json, so a computer with only Antigravity's ~/.gemini/config/mcp_config.json is offered Antigravity, not Gemini CLI.
  • agentlink owner add-agent --agent <name> (M1-9x, S2): when an owner is set up on this computer but no agent has a card from it (for example after every agent was removed), it no longer dead-ends between owner init and owner add-agent. Your owner code unlocks the saved owner, you confirm the owner fingerprint, and the agent gets its card. Same rules as before: your own terminal only, refused inside an AI app, the owner key is never changed.
  • Spec 04 §2.8 (M1-9x, S2, proposed, owner confirms at merge): new rule "No local card names the owner" (owner keys derived from the unlocked owner item, fingerprint confirmed before the one card is signed, a consistency check, not authentication); "Expected owner" names this case.
  • Windows file-path checks (M1-15-b, S2, 04 §4): the allowed-folder tests now run on Windows with no skipped test. Paths written with 8.3 short names are compared in their long form on both sides, a folder swapped for a link is refused check→open on every OS, and post-open where the OS allows it, and a Windows named pipe is refused before it is opened. No change to what is allowed or refused.
  • CLI (M1-22-b, S1, 06 §2.2): agentlink_inbox failed with an output-schema error for any message that carried terms, because the new termsParties field was not declared in the tool's output schema. It is declared now, and the inbox, status and contacts tests validate their results against the declared schema.
  • Dogfood script reports tool error codes (for example AMBIGUOUS_CONTACT) in the step notes, stops before the run when two contacts share the peer agent name, and addresses the peer by contact ID (M1-23-c, S1).
  • Accepting terms sometimes failed with an internal error instead of asking the owner (M1-32-d, S2, 04 §3.4.2): the approval request read the clock a second time, so a second boundary after the commitment scope was built made the request invalid. The request now starts at the scope's createdAt; a scope that expires while it is asked is reported as expired terms.
  • CLI (M1-9y, S1): agentlink needs Node.js 22.14 or newer (was 22.12). On an older Node it prints one line ("AgentLink needs Node.js 22.14 or newer; you have vX. Update Node ...") on stderr and exits, instead of crashing with a segmentation fault (seen on 22.13.1); this includes agentlink mcp, which never writes it to stdout. One-shot commands like owner init no longer print raw JSON info log lines; AGENTLINK_LOG=info|debug brings them back.
  • CI (OPS-CI-5, S2): a required path-guard job runs the Windows and POSIX path-guard tests on macOS and Windows runners and fails if they were skipped or did not run.