Skip to content

Have a Coding Agent Implement INK

If you’d like a coding agent (Claude, Codex, Cursor or similar) to do the bulk of an INK implementation for you, this page collects everything the agent needs in one place. It is framed as agent-assisted implementation, not as outsourcing trust to the agent. The generated code is a starting point that must pass the conformance vectors and survive a security review before any production traffic touches it.

If you are implementing INK by hand instead, the Implementing a Receiver guide and the Accepting Foreign Senders guide are written for human readers; come back here only if you decide to hand the work to an agent.

Where the authoritative lists live

This page does not copy the conformance categories, the MUST lists, the freshness windows or the rejection cases. Those change between releases, and a copy of them here would be a second source that goes stale without anyone noticing. Point the agent at the source, and make it read the source.

What the agent needsWhere it actually lives
Which conformance categories existconformance/v1/manifest.json, the machine-readable index of the corpus
Which categories a conforming implementation MUST satisfyspecs/ink-conformance-profile.md, which freezes the base profile and the capability-gated profiles
The per-requirement implementation matrixspecs/ink-compliance-checklist.md and the site’s compliance checklist
The normative specs in fullspecs/ in the INK repository
The prose spec for human and agent readingllms-full.txt, the concatenation of every page on this site

If any list on this page disagrees with those sources, the sources win. Tell the agent that in the prompt, and it is in the prompt below.

What you give the agent

A coding agent does its best work when it has the full normative material in front of it, not just a project README.

Spec bundle

  • The site corpus. llms-full.txt is the machine-readable concatenation of every spec, extension and reference page on this site, in a stable order. It fits in modern context windows.
  • The in-repo specs. llms-full.txt does not include the repository’s spec documents, and several of them are exactly what an implementer needs. Hand over specs/ as well, and call out two of them explicitly:
    • specs/ink-identity-model.md says what each INK principal identifies, when two identifiers denote the same actor, what each key role means and what a verified card does and does not prove.
    • specs/ink-resolver.md pins the walk from an identifier on the wire to verified key material: input classification, base derivation, URL construction, the request-side rules, the post-fetch verification order, caching and the outcome set. An agent that skips it will invent a resolver, and the invented one will be wrong in security-relevant ways.
  • Conformance corpus. The packaged conformance/v1 corpus, indexed by conformance/v1/manifest.json (see Test Vectors). It ships in the @adastracomputing/ink tarball. The manifest is the list of categories; read it rather than any prose summary of it, including this one. Each entry carries a profile field, and ink-conformance-profile.md says which profile binds which role. The implementation MUST make the same accept or reject decision as the reference on every vector in the base profile for the roles it performs, plus every capability-gated category matching a capability it advertises. Capabilities it does not implement MUST NOT be advertised.
  • Compliance checklist. The compliance checklist is the per-domain MUST and SHOULD list. The agent should treat it as acceptance criteria alongside the conformance profile.

Version claims

Two version numbers move independently and an agent will conflate them if you let it.

  • The wire version is the ink/<major>.<minor> string in the protocol field. See Versioning for which surfaces carry which value.
  • The package version of @adastracomputing/ink tracks the library, not the wire, and moves on releases that change no wire behavior at all.

A conformance claim is made against the profile and the declared capabilities, not against a package version. “We implement the base receiver profile plus the encryption capability” is a claim someone can check. “We are on the latest version” is not.

Security obligations

  • Receiver threat model. Accepting Foreign Senders covers the protocol and deployment requirements for accepting INK from agents whose identity lives on a different platform. Even if your service is single-tenant, the cryptographic and replay-protection sections are non-negotiable.
  • Wire-level checks. Signature verification, freshness, single-use nonce, recipient binding, JCS canonicalization and protocol-field shape enforcement, meaning every field the spec marks MUST is present, correctly typed and rejected when missing or malformed. These are MUSTs in Authentication, Replay Protection and Canonicalization. Take the freshness window from the spec, not from an example.
  • Resolution. Key material comes from a resolver, and the resolver is where SSRF, identity confusion and key substitution all live. From ink-resolver.md, four rules an agent gets wrong by default:
    • Refuse every redirect. Not just cross-host redirects. Discovery is served at a fixed path, so a redirect is not a card, and following one escapes the checks made against the original base. The refusal belongs at the transport layer.
    • Carry the port. A did:web identifier with a %3A-encoded port resolves at that port or is rejected. Silently dropping it retargets the fetch at a different origin, which is a different document.
    • Never take a base from the counterparty. A resolution base comes from trusted local configuration or from the spec’s own derivation rules. A base carried in the inbound envelope or in peer-supplied metadata lets a sender nominate the host that vouches for it, which makes the attacker both the question and the answer.
    • Use the versioned discovery path. GET <base>/ink/v1/<agentId>/agent.json is the sole normative discovery surface (ink-agent-card-discovery-fetch.md). An implementation MAY also serve /.well-known/ink/agent.json as an alias for the same document, and a resolver MUST NOT depend on it, reach for it first or fall back to it.
  • Host safety. Every outbound resolver request passes the classification in ink-private-hostname.md, failing closed on loopback, private, link-local, IANA special-use and malformed IP-shaped hosts. That classifier is a static-literal gate and it says so: the check MUST also run at connect time against the resolved address, with the connection pinned to the address that was checked. A resolver that cannot pin the connect address fails closed instead of calling the literal check an SSRF defense.
  • Agent Card signatures. Producer signing is in force: an implementation that serves a card signs it, and it publishes the members that make the proof checkable (ink-agent-card-signature.md §10, Phase B). A producer never emits a proof it cannot root. The receiver-side rejection of unsigned cards is Phase C, staged to begin no earlier than a documented minimum window after the Phase B release; §10 names the window and the release that starts the clock, so read it there rather than working from a date someone typed into a guide. Build Phase C now and hold it behind an explicit boolean switch, defaulted off. The switch MUST NOT be inferred from a package version or any other version string, and it MUST govern both Phase C decision points together.
  • Raw-byte verification. The signed-body verifiers take the raw request bytes, not a parsed value. The raw-body gate rejects invalid UTF-8, lone UTF-16 surrogate escapes, escaped object member names and number literals outside the IEEE-754 double range, and those are properties of bytes that no longer exist once the body is parsed (ink-signed-string-safety.md). An implementation that parses first and verifies the object accepts envelopes a conformant peer refuses, and a caller that hands the library a parsed value where it expects bytes gets a structural rejection, not a signature check.
  • Key-role binding on cards. A keys.signing entry decodes to the Ed25519 multicodec and a keys.encryption entry to X25519, each 32 bytes, with the algorithm label naming the role’s algorithm (identity model §4.1). A key in the wrong slot could never verify a signature or complete a key agreement, so validation rejects the card outright rather than leaving the mismatch to fail later. The agent-card conformance category pins both the accept and the reject side.
  • Structured error responses. Use the error codes verbatim on every reject path. A receiver that returns generic 400s loses the audit signal that tells operators what happened.
  • Encryption ordering and binding. On receive, verify the signature first, decrypt second, then check the inner identity claims against the outer envelope (Encryption). The binding is a producer obligation too: a conformant sealer MUST NOT emit an envelope its own decrypt rule would reject, so encryptInkPayload and its Go counterpart EncryptInkPayload refuse to seal a plaintext whose from and to do not match the outer envelope sender and the recipient identity (ink-payload-encryption.md). An implementation that checks this only on receive can still mint envelopes nobody will open.
  • Key rotation rules. Key Rotation. Once a card publishes a signing set, that set is authoritative and bootstrap derivation is never a fallback. Revoked keys never verify.

Reference implementations to read

  • @adastracomputing/ink is the canonical TypeScript library with the signing and verification primitives. The agent SHOULD prefer using it over re-implementing the wire format unless the goal is explicitly a from-scratch interop client.
  • go/ is the independent Go implementation. It is no longer verify-only: it exports SignInkBody and JCSCanonicalize, so it is a second sending implementation as well as a second verifier, and it runs the same conformance/v1 corpus. When a behavior matters for interop, the vectors pin the exact accept or reject outcome, so the agent can check a new implementation against them rather than inferring behavior from one codebase.
  • interop-lab/ runs a live exchange between the TypeScript and Go implementations in isolated containers, with each side producing its own bytes at run time and the other side deciding on them. It runs in CI on every push and pull request. Its README lists what each assertion proves and what it deliberately does not, which is the best available model for the cross-implementation smoke test your own implementation needs.
  • examples/foreign-sender-receiver/ is a self-contained TypeScript reference for the receive-side patterns, including the SSRF defenses and the inbound-foreign decision function. examples/reference-receiver/ is the receiver the interop lab drives.
  • examples/interop-cli/ is a Python sender that does not depend on @adastracomputing/ink: hand-rolled JCS, its own signature base, its own multibase encoding. It is useful reading for a from-scratch sender and it proves the wire contract is implementable cold. It is not the cross-implementation conformance peer, it resolves no DID method beyond did:key and its discovery check is a shape check rather than full card validation.

The implementer prompt

Hand the following prompt to your coding agent verbatim, along with the material above. The prompt is designed to keep the agent honest: read the source, refuse assumptions, write tests first and flag every place where the input is ambiguous.

Role. You are implementing the INK protocol for the codebase I will share. INK is an Ed25519-signed agent-to-agent protocol. The normative material is llms-full.txt, the specs/ directory of the INK repository and the conformance/v1 corpus. Treat that material as authoritative. If something is not in those documents, do NOT invent it. Surface the gap and ask me.

No copied lists. Any list of conformance categories, MUST items, timing windows or rejection cases that reaches you through a guide, a blog post or your own memory is a copy and may be stale. Derive every such list from the source: conformance/v1/manifest.json for categories, specs/ink-conformance-profile.md for which of them bind you, the compliance checklist for per-requirement obligations, the specs for everything else. Say which file you read for each.

Scope and acceptance criteria. Decide up front which roles you implement, sender, receiver or both, and which capabilities you advertise. Your obligation is the base profile for those roles plus the capability-gated categories for every capability you advertise. Do NOT advertise a capability you have not fully implemented. Do NOT phrase a conformance claim as a library version. Document the scope in the traceability matrix below, and do not mark an item complete until its test passes.

Scope loopholes to refuse. If you mark encrypted-payload receive as out-of-scope, your endpoint MUST still reject plaintext envelopes carrying the intents the protocol marks confidential. Accepting them in plaintext is a spec violation, not a scope reduction. The same rule applies anywhere a feature is skipped while its required failure modes remain mandatory.

Traceability matrix (mandatory). Before writing any code, produce a table mapping every applicable MUST to one of implemented / out-of-scope / blocked, with a spec citation and the file path of the test that will cover it. Re-emit the table after each iteration. Do NOT silently narrow scope by leaving items off the table.

Workflow.

  1. Read the sources. Produce the traceability matrix.
  2. For each implemented MUST, write the failing test first, then implement, then confirm green.
  3. For resolution and outbound delivery, follow specs/ink-resolver.md step by step and treat the foreign-sender-receiver example as the structural reference for which defenses are required.
  4. Cite the spec section in commit messages, in the PR summary, in test names or descriptions AND in code comments on every security-sensitive check. The citation is what lets a future reviewer audit your reasoning. Many agent workflows do not produce meaningful commit history, so multiple citation surfaces are required, not just commits.
  5. After every implementation step, re-emit the matrix, run the full test suite and walk the checklists below.

Rules of engagement.

  • Never let a resolver make an outbound request to a private, loopback, link-local, IANA special-use or malformed IP-shaped host, and apply the check at connect time as well as against the literal, pinning the connection to the address you checked. This applies to every resolver fetch you add, not only the did:web: one.
  • Never follow a redirect on a discovery fetch, same-host or not.
  • Never drop a %3A-encoded port from a did:web identifier. Carry it into the derived origin or reject the identifier.
  • Never derive a resolution base from anything the counterparty supplied, including the message you are currently trying to verify.
  • Never depend on /.well-known/ink/agent.json. The versioned path is the discovery surface.
  • Never compare identifiers with startsWith against a method prefix without the trailing colon. Require the canonical did:<method>: shape, and canonicalize before comparing.
  • Never verify a signed body from a parsed value. The verifier’s input is the raw bytes; parse only what the verifier hands back.
  • Never place key material in a card role slot whose multicodec it does not carry. Signing keys decode to Ed25519, encryption keys to X25519, and validation rejects the card otherwise.
  • Never log envelope payloads, nonces or secret material into observability pipelines.
  • Never trust risk-scoring verdicts as authorization. Cryptographic checks are authoritative; scoring is advisory.
  • Never reverse the encryption ordering. Verify signature, decrypt, then check inner against outer. Enforce the same binding at seal time so you cannot emit an envelope a conformant peer would refuse.
  • Never serve an unsigned card you could have rooted, and never emit a card proof you cannot root.
  • Use the structured error codes on every reject. Do not invent reason strings.
  • When a test fails, report it. Do not silently relax the test.
  • When a spec section is materially ambiguous, meaning two readings have different security or wire consequences, mark it blocked in the matrix and stop. Harmless local decisions such as naming and file layout are documented in the matrix with a one-line note, not blocked on.

Starter traceability matrix

The matrix below is a shape, not a scope. It shows one representative row per domain so the agent knows what a row looks like; the actual rows come from the compliance checklist and the conformance profile for the roles and capabilities you declared. A real matrix is several times longer than this.

The statuses below are illustrative. The agent MUST re-evaluate every status against the actual codebase and replace them all before writing any code.

IDMUST itemScope statusSourcePlanned test
AUTH-01Verify the transport signature over the spec’s signature base on every inbound messageimplementedAuthentication; signature-basetest/ink/signature-base.test.ts
AUTH-02Parse the Authorization header under the exact grammar, rejecting malformed variantsimplementedAuthentication; authorization-headertest/ink/auth-header.test.ts
KEYS-01Apply the key-rotation authority rule by key status and validity windowblockedKey Rotation; key-rotationtest/ink/key-rotation.test.ts
REPLAY-01Enforce the freshness window and reject a replayed nonce, recording the nonce lastimplementedReplay Protection; replay-freshnesstest/ink/replay.test.ts
CANON-01Canonicalize with RFC 8785 JCS under the number and string-safety profilesimplementedCanonicalization; the canonicalization and signed-body categories in the manifesttest/ink/jcs-vectors.test.ts
RESOLVE-01Refuse every redirect, carry a %3A port, never accept a counterparty-supplied baseimplementedink-resolver.md §3test/ink/resolver.test.ts
DISC-01Fetch the card at the versioned path and apply the response contract in orderimplementedink-agent-card-discovery-fetch.md; agent-card-fetchtest/ink/card-fetch.test.ts
SSRF-01Gate every outbound resolver URL, at literal and at connect timeimplementedink-private-hostname.md; private-hostnametest/ink/host-safety.test.ts
CARD-01Sign every card served, rooted by principal kind; verify a present proof and hold Phase C behind a switchimplementedink-agent-card-signature.md; agent-card-signaturetest/ink/card-signature.test.ts
ENC-01Reject plaintext envelopes for confidential intentsimplementedEncryptiontest/ink/plaintext-confidential-intents.test.ts
ENC-02Bind inner to outer identity on both the seal and the open pathout-of-scopeEncryption; payload-encryptionn/a
GRANT-01Verify a scoped, audience-bound, expiring authorization grant before acting on itout-of-scopeAgent Authorization; authorization-grantn/a

implemented rows must have a passing test. blocked rows must name the unresolved ambiguity. out-of-scope rows must NOT silently widen acceptance: ENC-01 stays implemented even where ENC-02 is out of scope, because the rejection is mandatory whether or not a decrypt path exists.

Conformance checklist for the agent’s output

Run these before merging. The agent should also run them itself between iterations.

  • A current traceability matrix exists, with every applicable MUST mapped to implemented / out-of-scope / blocked, each with a source citation and a test file path. Nothing is missing from the table.
  • The declared roles and capabilities are written down somewhere a user of the implementation will see, so it never reads as a broader claim than it is.
  • Every base-profile category for the declared roles, plus every capability-gated category for a declared capability, decides accept or reject the same way the reference does. Read the category set from conformance/v1/manifest.json and the profile split from ink-conformance-profile.md, not from any list transcribed elsewhere.
  • A live cross-implementation exchange passes for every send and receive capability declared, not just the fixture corpus. The corpus proves both sides agree on fixed bytes; only a live exchange proves they agree when each side produces its own. interop-lab/ is the working model, including its list of what such a lab does not prove.
  • Structured error codes. Every reject path returns one of the codes from /reference/errors/. No invented or generic strings.
  • Protocol-field shape enforcement. Every field the spec marks MUST is present, correctly typed and rejected when missing or malformed.
  • Encryption ordering and binding. Signature first, decrypt second, inner-against-outer third, with tests for the wrong order and for a forged inner identity. The seal path refuses the same mismatch.
  • Resolver. Tests cover a refused redirect, a did:web identifier carrying a %3A port, a base offered by the counterparty, a non-https base, a base carrying userinfo and a card served under a different identity than the one requested. The expected outcomes are in ink-resolver.md §3 and §6.
  • Card signatures. A served card is signed and rootable. A present proof is verified, an invalid proof rejects outright, and the ratchet and continuity rules hold against a cached prior card. Phase C sits behind an explicit switch that is off by default and governs both of its decision points together.
  • Host safety. The literal classifier and the connect-time check both run, and the implementation fails closed where it cannot pin the connect address.
  • Signature verification rejects the failure cases the vectors enumerate. Do not settle for the subset a guide happens to name.
  • Allow-list and block-list matching rejects prefix confusion (partner.example against evilpartner.example), trailing dots, mixed case and method prefixes without a trailing colon.
  • Failure modes: every required check fails closed, and risk scoring is advisory unless explicitly enforced.
  • Observability: no secrets, full payloads or nonces in logs.

Human review checklist

Before any production traffic, a human MUST sign off on:

  • The agent followed the workflow above, with tests first and spec citations in commits, the PR summary, test names and code comments on every security-sensitive check.
  • Every list the agent worked from was derived from the repository sources rather than from a summary, and the agent named the file it read for each.
  • No unimplemented or TODO markers remain in security-relevant code paths.
  • Integration tests exercise real card fetches, not mocked ones, against at least one fixture server, including at least one rejection path.
  • The agent flagged every place where the spec was ambiguous, and each flag has a documented resolution.
  • At least one round of static analysis or dependency audit has been run.
  • If the implementation receives traffic from foreign identities, a security review specifically of the resolution, SSRF and identity-binding surfaces has been performed.

What this page does not do

It does not certify the agent’s output. It does not promise that following the prompt produces a compliant or secure implementation. It collects the right materials so that an agent can do the work, and the conformance checklist plus the human review are how you confirm the agent actually did.

If your implementation passes both checklists, please consider opening a discussion describing what worked and what didn’t. The packet on this page is more useful with feedback from real agent-assisted runs.