Releases: Ad-Astra-Computing/ink
v0.4.0
This release tightens signature verification and input validation and adds
several verification helpers. It is published on the next dist-tag.
Potentially breaking validation tightenings
These reject inputs that 0.3.0 accepted. Legitimate signer and receiver
traffic is unaffected; the rejected inputs are malformed, malicious, or outside
the documented profile.
- Ed25519 signatures are now verified in strict RFC 8032 mode at every
verification site. Small-order public keys and non-canonical point encodings
are rejected. - Signed JSON numbers are constrained to the forms every canonicalizer
serializes identically: non-finite values, negative zero, and values whose
shortest form uses exponential notation are rejected at signing and
verification. - The agent card, audit, handshake, and discovery schemas now enforce maximum
field lengths and array sizes. - The
Authorization: INK-Ed25519header is matched against single literal
spaces; a tab, carriage return, or line feed in the separator is rejected.
Additions
verifyCheckpoint(signed, witnessPublicKey, expectedOrigin)verifies a signed
C2SP checkpoint: the witness Ed25519 signature over the checkpoint body and the
log origin. A checkpoint used for the inclusion-receipt cross-check must be
verified this way first.verifyReceipt({ receipt, senderPublicKey, expected })binds a delivery
receipt to the exact message it acknowledges: issuer key,from/to/
messageId, the recomputed message hash, and an optionaldisposition.verifyInclusionReceiptaccepts aneventoption that recomputes the leaf
hash and binds it toreceipt.eventId. The legacyeventHashis retained but
does not provide that binding.verifyInkAuthreturns a prefix-independentprincipalalongside the raw
sender id; per-sender security state (blocks, rate limits) should key on
principal.canonicalAgentPrincipal(agentId)is exported for the same use.
Per the pre-1.0 policy this release publishes under the next dist-tag; latest
is unchanged.
v0.3.0
extractPublicKeyFromAgentId now accepts either the canonical tulpa: prefix or the ink: alias introduced in ink/0.4. Both carry the identical multibase Ed25519 key, so the bootstrap verification key is byte-identical and a signature made with that key verifies regardless of which accepted prefix carried it. The prefix is identity syntax, not signing authority.
Emission is unchanged: deriveAgentId still returns tulpa: (accept both, emit one). The new AGENT_ID_KEY_PREFIXES export is frozen so a consumer cannot widen the accepted set at runtime. The change is additive and backward compatible. Existing tulpa: inputs behave exactly as before, and every previously rejected prefix other than ink: is still rejected. The wire protocol version is unchanged.
A receiver that keys per-sender security state (blocks, rate limits, duplicate-payload checks, cached verification keys, connection identity) MUST collapse the two spellings to one prefix-independent principal so a sender cannot switch prefix to dodge a block or split a rate-limit window. See Identity.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.2.0
Version-keyed body-signature domain. The body message signature is now domain-separated by protocol version. ink/0.1 messages, and any object with no explicit ink/0.2 protocol, keep the legacy tulpa/sign domain so every signature produced to date still verifies. ink/0.2 messages are signed and verified under the neutral ink/sign domain. The verifier selects exactly one domain from the signed protocol field and never tries an alternate, so a signature made under one version's domain cannot be replayed under another.
This change is receiver-first and backward compatible. Verifiers accept both versions and MessageEnvelopeSchema now accepts ink/0.1 and ink/0.2 as a strict enum, rejecting any unknown version. Senders still emit ink/0.1 by default. The HTTP transport-auth signature is unchanged.
New vectors in test-vectors/body-signature.json pin the version-keyed domain including the cross-version and tamper cases. The standalone Python interop client verifies them identically.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.1.7
Pure additive release. Re-exports every per-intent Zod payload schema (ScheduleMeetingPayloadSchema, IntroRequestPayloadSchema, OpportunityPayloadSchema, ConnectionRequestPayloadSchema, FollowUpPayloadSchema, AskPayloadSchema, PingPayloadSchema, RetractPayloadSchema, ContextSharePayloadSchema, MultiPartySyncPayloadSchema, plus the matching *ResponsePayloadSchema variants) and the getPayloadSchema(intent) resolver from the package root. Adopters writing intent-aware receivers / handlers can now type their dispatch surface directly against the canonical payload shapes.
No wire-level changes. No behavior changes inside the existing functions. Receivers on 0.1.6 work unchanged on 0.1.7.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.1.6
Pure additive release. Two surface expansions and one backward-compatible schema addition:
- Intent surface — re-exports
IntentTypeSchemaconstant andIntentTypetype from the package root. Adopters writing payload-aware receivers can now type their intent dispatch off the canonical Zod enum without reaching into a deep path. - Key-entry surface — re-exports
KeyStatusSchema,KeyRoleSchema,KeyEntrySchemaconstants andKeyStatus,KeyRole,KeyEntry,StoredKeytypes. Adopters wiring their own key-set storage and rotation can now type the persistence shapes without reaching into a deep path.CandidateKeywas already root-exported. - InkAuditInclusionSchema — adds an optional
inclusionProof: z.array(z.string()).optional()field. Third-party auditor clients that verify Merkle inclusion proofs use this field; receivers that only check signatures can ignore it. Backward-compatible because the field is optional — receivers on 0.1.5 work unchanged on 0.1.6.
No wire-level changes. No behavior changes inside the existing functions.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.1.5
Pure additive release. Re-exports the InkChallenge, InkRejection, InkResolution, InkTransport types and the InkTransportSchema constant from the package root. Adopters writing handshake-aware receivers can now type their state-machine without reaching into a deep path. No wire-level changes. No behavior changes inside the existing functions. Receivers on 0.1.4 work unchanged on 0.1.5.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.1.4
Pure additive release. The implementation files have shipped in earlier releases under deep paths; this release brings them to the package root so adopters writing receivers, builders, or auditor clients can import everything they need from @adastracomputing/ink directly.
New root-level exports:
- Receipts (
./ink/receipts):buildReceipt,shouldSendReceipt,sendReceiptFireAndForget. The canonical INK delivery-receipt builders, signing helpers and fire-and-forget transport. A receiver that wants to ack inbound envelopes per Auditability §6 can drop these in without rolling its own. - Transport-auth (
./ink/transport-auth):resolveEffectiveTransports,checkTransportAllowed. The token-level transport allowlist enforcement, including the "field absent vs empty array" semantics. Required for any extension token issuer. - Discovery-gating (
./ink/discovery-gating):buildRedactedCard,shouldRedactOnGet,AgentCardQuerySchema. Visibility-aware Agent Card responses (public,network_only,capability_gated,private). - Checkpoint parsing (
./ink/checkpoint):parseCheckpoint,formatCheckpoint,CheckpointData. For consumers of transparency-log signed checkpoints. - Audit event schemas + types (
./models/ink-audit):InkAuditEventTypeSchema,InkAuditEventSchema,InkAuditInclusionSchema,InkReceiptSchema,InkAuditQuerySchema,InkIntroductionReceiptSchema, plus matching typesInkAuditEventType,InkAuditEvent,InkAuditInclusion,InkReceipt,InkAuditQuery,InkAuditResponse,InkIntroductionReceiptStatus. - Handshake message schemas (
./models/ink-handshake):InkChallengeSchema,InkRejectionSchema,InkResolutionSchema, typeAgentCardVisibility. - Agent Card schema (
./models/agent-card):AgentCardSchema(the type was already exported). - Encryption key encoder (
./crypto/keys):encodeEncryptionKeyMultibase(the encoder companion to the already-exporteddecodeEncryptionKeyMultibasefrom 0.1.3).
No wire-level changes. No behavior changes inside the existing functions. Receivers on 0.1.3 work unchanged on 0.1.4.
Per the pre-1.0 policy this release publishes under the next dist-tag.
v0.1.3
Pure additive release that re-exports two helpers from the package root so adopters no longer have to import them through a deep internal path:
validateMessage(raw)runs the canonicalMessageEnvelopeSchemaparse plus the intent-specific payload schema. Receivers building from scratch were either re-implementing the schema check or pulling from@adastracomputing/ink/dist/models/intent.js, which is not a stable surface. The implementer-guide at https://ink.tulpa.network/guides/implementing-a-receiver/ documented this helper as if it were already exported; this release makes that documentation accurate.decodeEncryptionKeyMultibase(multibase)is the companion to the already-exporteddecodePublicKeyMultibase. The former handles X25519 keys (the Agent Card encryption-key prefix); the latter handles Ed25519. The encrypted-intents guide tells adopters to decode an Agent Card'spublicKeyMultibasefor use withencryptInkPayload, which expects hex; without the X25519 decoder exported, adopters had to inline the multicodec strip themselves.
Also re-exports the MessageEnvelope type and the MessageEnvelopeSchema constant so adopters can type their parser surface against the canonical schema. No wire-level changes. No behavior changes inside the existing functions. Receivers on 0.1.2 work unchanged on 0.1.3.
This release publishes under the npm next dist-tag per the pre-1.0 policy.
v0.1.2
Maturity note. v0.1.x is wire-compatible across patches (
ink/0.1stays frozen) but the API surface and trust semantics remain alpha-quality. Seedocs/maturity.md. Starting with this release, pre-1.0 versions publish under npm'snextdist-tag;latestonly advances when a release is explicitly promoted. Adopters who want the current pre-1.0 line install withnpm install @adastracomputing/ink@next; the barenpm install @adastracomputing/inkwill resolve to the most recent release a maintainer has stamped adopter-grade.
Fixes the v0.1.1 erratum: the Python examples/interop-cli/ shipped in v0.1.1 emitted a phantom envelope shape (type, intentType, purpose, urgency at top level, no id, no correlationId, no createdAt, no body-level signature) that no conforming receiver could accept. v0.1.2 rewrites the CLI's envelope builder to emit the canonical MessageEnvelopeSchema shape:
idandcorrelationIdare now generated as 26-char Crockford-base32 ULIDs.createdAtis the canonical envelope creation timestamp (ISO-8601 UTC); the body also carries a separatetimestampfield thatverifyInkAuth()uses for HTTP §3.3 freshness, distinct fromcreatedAt.intentis the canonical enum value (intro_requestfor introductions); the legacyintentType/purposeflatten intopayload: { target, reason, urgency }perIntroRequestPayloadSchema.- Body-level
signatureis now produced by the canonical domain-separated signer (Ed25519("tulpa/sign\n" + JCS(envelope-without-signature)), base64url, no padding) — matchessrc/crypto/sign.tsbyte-for-byte. provenanceis omitted when absent (the field is.optional(); an explicitnullwould be rejected by Zod).- HTTP §3.3 fields (
timestamp,nonce) ride alongside the canonical fields soverifyInkAuth()still reads them.
CLI now builds connection_request envelopes. ink-interop send/build --intent-type connection_request (or the alias connection) constructs a ConnectionRequestPayloadSchema-conformant payload (method, context, profileSnapshot). This is the bootstrap intent for first contact between strangers: receivers that opt in to foreign senders verify the body signature against the inline key extracted from the sender's did:key (trust-on-first-use). Other intent types (intro_request, ask, follow_up) presume the sender is already a known contact and remain reserved for established relationships.
Verified end-to-end against https://api.tulpa.network/ink/v1/<agentId>/intent: a did:key: connection_request from ink-interop send lands as a pending action in the recipient's inbox (status: 200, accepted: true, pendingActionId: 01KT…). Coverage spans schema validation, body + transport signature verification, replay/freshness, identity resolution, routing, the foreign-DID policy gate, and shield risk-scoring. Tests pinned to the canonical shape (tests/test_envelope.py) prevent regression. The npm library itself is unchanged from v0.1.1.
Example-helper API break. examples/interop-cli/'s Python helper build_intent_envelope() now requires keypair, replaces intent_type/purpose/timestamp with canonical args (target, reason, created_at, etc.), and removes the extra= kwarg. Adopters who imported the old helper directly will need to update their calls — the previous signature emitted invalid wire data so no callable interop existed there to preserve. This is an example-only change; the npm library (@adastracomputing/ink) exports are unchanged.
v0.1.1
Erratum, 2026-06-01: the bundled Python interop CLI at
examples/interop-cli/shipped with this tag emits an envelope
shape that does not match the canonicalMessageEnvelopeSchema
(id,correlationId,createdAt,intentenum, payload,
body-levelsignature). The npm library is unaffected. End-to-end
sends from the Python CLI to a conforming receiver fail with
invalid_envelope. Fixed in v0.1.2.
This release is wire-compatible with v0.1.0; the wire version stays ink/0.1. Every change below is additive and accepts the prior shape for the duration of the v0.1.x line — implementations that emit and consume v0.1.0 cards / service entries continue to interoperate. One observable library behavior changes: the runtime emit value of the redacted Agent Card type field flips from "tulpa.agent.card" to "ink.agent.card" (see below). Consumers that pinned to the literal must accept either string; the TypeScript union has been widened accordingly.
Service entry rename. The DID Document service entry for INK endpoints is now type: "INKAgentEndpoint". The legacy "TulpaAgentEndpoint" is still accepted by consumers during v0.1.x; new publishers SHOULD emit INKAgentEndpoint. When both are present, INKAgentEndpoint takes precedence. Removed at the next wire-version bump.
Service entry now points at the Agent Card URL. serviceEndpoint is the URL of the Agent Card, not the inbound message endpoint. Inbound URL stays on the Card itself in the endpoint field. Per the spec update, inboxEndpoint is also accepted as a synonym for endpoint.
Normative SSRF floor for discovery fetches. The Discovery spec now mandates HTTPS-only, refusal of private/link-local/loopback/cloud-metadata hosts, bounded redirects with host re-checking on each hop, response size and time caps, and honoring Cache-Control. Detailed hardening stays implementer responsibility but the normative floor lives in the spec.
Normative DID-binding of fetched cards. Consumers MUST bind the card's ownerDid (when present) to the DID under resolution, and bind the card's agentId to the agent identifier being sent to. A mismatch on either field is a hard reject. This closes the substitution attack where a host that legitimately publishes one DID claims to publish another.
Cache and refresh rules lifted into normative Discovery. Refresh on signature/keyId miss, on observed keySetVersion increase, never fall back to bootstrap keys after a valid card has been observed.
Redacted Agent Card type renamed. The type field on a redacted Agent Card is now "ink.agent.card". Consumers MUST also accept the legacy "tulpa.agent.card" during v0.1.x. The network.tulpa.* wire-message types are explicitly frozen for v0.x per the compatibility policy; rewriting them would break every deployed router.
Wire version is ink/0.1. All v0.1.x package releases emit protocol: "ink/0.1" on the wire. The next wire-version bump is ink/0.2.