| Internet-Draft | DTL | July 2026 |
| Phelan | Expires 3 January 2027 | [Page] |
DTL (Dyadic Typed Layer) defines a single, minimal primitive: a typed, directed relationship between two agents that the controller of each agent has confirmed, together with the lifecycle of that relationship. The name describes the primitive: Dyadic (it concerns one pair of parties), Typed (the relationship carries exactly one kind), and Layer (it sits above an identity substrate, not inside it).¶
Agent ecosystems have built out several coordination layers: identity (who an agent is), reputation and attestation (what is asserted about an agent), and discovery and messaging (how agents find and talk to each other). One primitive is comparatively underserved, the relationship: a standing, acknowledged, directed connection between two specific agents, recording that they are related and in what capacity. No existing standard, to the author's knowledge, provides exactly this primitive in this form. DTL specifies it and nothing more. It is normative about the mechanism (a typed, directed, two-party, controller-confirmed edge and its lifecycle) and deliberately silent about substrate, transport, storage, and the open vocabulary of relationship types. An informative binding to ERC-8004 is provided as the first anchoring substrate.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 2 January 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Autonomous software agents increasingly act on behalf of different principals and across organizational boundaries. Several coordination layers are now actively addressed:¶
Identity and attestation are one-way: one party makes a claim about, or an identifier for, another. Discovery and messaging concern communication, not a standing bond. None captures a relationship: a connection between two specific agents that both sides acknowledge, in a specific capacity, with a direction: "agent A delegates to agent B," "agent A may message agent B," "agent A depends on agent B."¶
The constructs closest to this are either one-way (attestations, reputation, and recent verifiable-delegation schemes) or relationship-shaped but not typed for the standing relationship (peer connections that record that two parties are connected, where any type present describes the connecting handshake rather than the kind of standing relationship that results). The gap is a connection that is at once acknowledged by both parties, directed, typed, and lifecycle-bearing.¶
This is a small but load-bearing unit of coordination. Google DeepMind's June 2026 multi-agent-safety funding call highlighted identity, reputation, and commitment among the priorities for secure cross-platform agent interactions, the same problem space DTL addresses. "Commitment" is used here in the broad sense of a standing, acknowledged relationship, not the narrower game-theoretic sense of a commitment device that binds an agent's future choices (as studied in the cooperative-AI literature on decentralized commitment devices). DTL specifies the smallest useful version of a standing relationship: one confirmed, typed, directed relationship between two agents.¶
In scope. The single pairwise relationship: its record, its one type, its lifecycle, and the semantics of confirmation. This is the dyad: exactly two parties.¶
Out of scope.¶
signed profile, and the
resolution of conflicting or concurrent actions (e.g. a total order
across replicas) are binding- and deployment-defined, not specified
here, and are a known area for a future version. A binding
(Section 7) supplies the concrete, interoperable
profile against which independent implementations interoperate.¶
General in mechanism, specific in framing. The mechanism is defined over generic parties that have a controller; this specification frames and motivates it around AI agents as the primary case. A DTL agent may be software or embodied (e.g. robotics); DTL operates above the agent's internal cognitive architecture and embodiment, and makes no assumption about how an agent is built. The following are deliberately left as non-goals for v1, noted only for forward compatibility (the mechanism admits them, but they are not specified here):¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
A relationship is represented by a relationship record, a JSON [RFC8259] object. A record MUST contain:¶
sourcetargettarget MUST NOT equal source (no
self-relationship).¶
typestatusproposed, established, declined,
withdrawn, revoked.¶
proofsstatus
(Section 6); Appendix A specifies the
required entries per state.¶
profileA record MAY carry an id, timestamps (created_at,
updated_at), a free-form human-readable description, and
an ext object holding deployment- or substrate-specific
extension fields. The record's top-level fields are otherwise closed
(Appendix A); ext is the single designated place for
fields this specification does not define, and its contents are
unconstrained.¶
A relationship is directed: source -> target is significant and is not
symmetric. "A delegates_to B" is a different relationship from "B
delegates_to A." A verifier MUST NOT treat a relationship as
symmetric.¶
The constraint that source and target MUST differ (no
self-relationship) is normative but is not expressible in JSON Schema:
Draft 2020-12 cannot compare two instance values to each other.
Implementations MUST enforce it outside the schema.¶
A normative JSON Schema for the record is given in Appendix A.¶
A relationship MUST carry exactly one type.¶
The type vocabulary is open: type is a free-form
string, and implementations MAY define their own types. This
specification is normative about typed-ness (a relationship carries
exactly one type) but NOT about the set of legal type values. A
type is a descriptive label on the record: it names what the
relationship asserts and is not prescriptive; this specification attaches
no behavioral semantics to any type, and a type by itself grants no
capability and triggers no enforcement (Section 8). It is
a label, not a directive.¶
Recommended vocabulary (informative). Implementations are encouraged, for interoperability, to use the following bare identifiers where they apply:¶
delegates_tocan_send_message_todepends_onNamespacing (SHOULD). To avoid collisions in the open vocabulary: bare
identifiers are reserved for types defined by this specification (the
recommended vocabulary above). Any other type SHOULD be namespaced as a
URI, for example https://example.com/dtl/audits, following the
convention [RFC8288] (Web Linking) uses for extension
relation types, which are themselves URIs.¶
A relationship moves through the following states:¶
confirm
proposed --------------------------> established
| | |
| | withdraw | revoke
| | (source) | (either party)
| v v
| withdrawn (terminal) revoked (terminal)
|
| decline (target)
v
declined (terminal)
proposed.¶
proposed, the target's
controller either confirms it (-> established) or declines it
(-> declined); independently, the source's controller MAY
withdraw its own still-unconfirmed proposal (->
withdrawn).¶
established relationship MAY be revoked (->
revoked) by either party's controller.¶
withdrawn, declined, and revoked are
terminal. A new relationship between the same parties MAY be proposed
afresh.¶
A single principle governs every exit: a controller may unilaterally retract its own authorization at any nonterminal stage in which it still holds that authorization; the change takes effect immediately and is attributable to that controller. Before confirmation only the source has authorized, so only the source can unwind the proposal (withdraw), while the target unwinds simply by declining rather than confirming. After confirmation both parties have authorized, so either may unwind (revoke). Forming a relationship requires both sides; exiting one never does: requiring mutual consent to end a relationship would let one party trap the other in a stale edge.¶
Multiplicity (deployment-defined). This specification defines the
lifecycle of a single relationship record. Whether two parties may
simultaneously hold several relationships of the same type, and
whether proposing afresh supersedes a terminal prior record or coexists
with it, are deployment-defined and out of scope.¶
The defining property of DTL is confirmation by both parties: a
relationship reaches established only when the controller of each
party has authorized it, the source by proposing, the target by
confirming. This two-party authorization is exactly what distinguishes a
relationship from a one-way attestation. These are two endpoint-role
authorizations, one for the source role, one for the target. DTL does not
require the two controllers to be distinct; where a single controller
holds both agents it authorizes both roles. The structural distinction
DTL draws is two endpoint-role authorizations, not necessarily two
independent parties.¶
Each authorization is a proof of authorization. What counts as a valid proof, and who can verify it, is set by the confirmation profile in force for the record. This specification defines two profiles and permits others.¶
The proofs of a record form an append-only set of
authorizations, at most one per action, accrued as the record advances
and never rewritten: a record MUST carry a proof of authorization for
every lifecycle action (Section 5) that has brought it to
its current status. A proposed record carries a
proposal proof; an established record carries
proposal and confirmation; a declined record
carries proposal and decline; a withdrawn
record carries proposal and withdrawal; and a
revoked record carries proposal, confirmation,
and revocation. Appendix A encodes these per-status
requirements normatively, but constrains only which proofs are present,
not their contents: the internal structure of each proof is
profile-defined, so schema-validity does NOT imply proof-satisfaction. As
with the requirement that source and target differ
(Section 3), proof attribution (that a proof was genuinely
produced by the named controller) is not expressible in JSON Schema and
is the responsibility of the profile and the verifier.¶
attested profile.method field (e.g. siwe, as in
Appendix B), that field describes the attester's
authentication process only; it confers no independent verifiability,
and only the signed profile yields proofs a third party can
check without trusting the attesting service. (Example: each controller
authenticates to a platform, and the platform records the
authorization.)¶
signed profile.did:key), but requiring a
lookup for methods that resolve material over the network or a ledger
(e.g. did:web, on-chain keys). Its integrity rests on the
controller's verification material rather than on a trusted
service.¶
A record MUST declare which profile produced its proofs. Profiles are extensible: a deployment MAY define additional profiles, provided each specifies how proofs are produced and verified and what trust they convey.¶
Profiles vs bindings. The two profiles defined here are trust categories, not wire-complete schemes: they classify the trust a proof conveys but do not by themselves fix a proof's contents, its canonical signing input, or a verification procedure. A concrete, interoperable profile (one against which two independent implementations can produce and check the same proofs) is supplied by a binding to a specific substrate (the ERC-8004 binding of Section 7 is the first). Canonicalization, signing input, and the verification procedure are therefore binding- and deployment-defined (a non-goal for v0; see Section 1.1).¶
Field casing (informative). Fields defined by this specification
(record-level fields and the fields of the attested profile's
proofs) use snake_case (e.g. created_at, attested_by).
Fields adopted from an external standard inside a proofs object
retain that standard's convention, for example
verificationMethod keeps the camelCase form specified by W3C
DID.¶
Autonomy (informative). In the
signedprofile the controller that produces a proof MAY be the agent itself, when the agent holds its own verification material. This is the path by which a relationship can be formed with no human in the loop: the mechanism is neutral as to whether a controller is a human, an organization, or the agent itself.¶
This binding describes how DTL maps onto ERC-8004 [ERC-8004] ("Trustless Agents"), the first substrate on which it is anchored. It is informative; nothing here is normative for DTL.¶
chainId:agentId (e.g. 11155111:123) for unambiguous
cross-chain reference.¶
attested profile:
each controller authenticates with Sign-In with Ethereum (SIWE)
[EIP-4361], the target confirms through a single-use
confirmation link, and the service records both authorizations. A
signed profile (direct signatures by each owner over the
record) is the natural trustless extension and is not yet
implemented.¶
verified -> established; rejected ->
declined; proposed and pending (proposal
initiated, awaiting the target) -> proposed. The implementation
additionally has an inferred seeding state for candidate
relationships that carry no controller authorization; these fall
outside the DTL lifecycle and are not DTL relationships until proposed
and confirmed. DTL's withdrawn and revoked states are
defined by this specification but are not yet implemented in the
reference deployment.¶
delegates_to, can_send_message_to,
depends_on) plus supervises and complements.
The latter two are lower-confidence (supervises is awkward
across organizational boundaries; complements is the least
precisely observable) and are NOT part of the recommended vocabulary in
Section 4. Note also that complements reads as
symmetric, whereas DTL records are directed (Section 3); a
mutual "complements" relationship is therefore represented as two
explicit directed records, not one.¶
established relationship depends
entirely on its confirmation profile. An attested relationship
is only as trustworthy as its attesting service and is not portable to
parties that do not trust that service; a signed relationship
can be verified without trusting an attesting service, though still
contingent on obtaining and trusting the controllers' verification
material (and the resolver, DNS, or ledger through which it is
published).¶
established, and it constrains that lifetime
only to the extent a verifier can observe current state: a stale or
cached record may not yet reflect a revocation that has occurred.¶
delegates_to relationship does not confer
permission; enforcement is the responsibility of the systems that act
on the relationship and is out of scope.¶
source,
target, type) and, where a deployment permits more
than one record for the same source/target/type
over time (Section 5), a value that distinguishes the
specific record instance (such as its id or a per-proof nonce).
Where such multiplicity is permitted, binding to the instance is
REQUIRED, so that an old proof from a since-terminated relationship
cannot be replayed onto a fresh proposal of the same triple; otherwise
it SHOULD be present.¶
source/target ordering and MUST NOT infer the
reverse relationship from a record.¶
proposed record carries only the source's authorization;
a verifier MUST NOT read it as the target's agreement. Until
confirmation it is an unaccepted offer, and a deployment SHOULD guard
against unsolicited-proposal ("proposal spam") abuse.¶
signed profile;
controller rotation (a relationship authorized under a key or owner that
later changes); domain separation (ensuring a proof minted for one
purpose, type, or deployment cannot be reinterpreted in another); and
freshness / stale state (a verifier acting on an out-of-date record).
DTL specifies the relationship and its lifecycle; closing these is
binding- and deployment-specific work.¶
This document has no IANA actions.¶
This appendix gives the normative JSON [RFC8259] Schema [JSON-SCHEMA] for the relationship record (Section 3).¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://opendtl.org/v0/relationship.schema.json",
"title": "DTL Relationship Record",
"$comment": "The constraint source != target is normative (see section 3) but cannot be expressed in JSON Schema; implementations MUST enforce it externally.",
"type": "object",
"required": ["source", "target", "type", "status", "profile", "proofs"],
"properties": {
"id": {
"type": "string",
"description": "Optional unique identifier for the relationship record."
},
"source": {
"type": "string",
"minLength": 1,
"description": "Identifier of the source agent (substrate-defined)."
},
"target": {
"type": "string",
"minLength": 1,
"description": "Identifier of the target agent (substrate-defined). Must differ from source."
},
"type": {
"type": "string",
"minLength": 1,
"description": "Exactly one relationship type, from an open vocabulary."
},
"status": {
"type": "string",
"enum": ["proposed", "established", "declined", "withdrawn", "revoked"]
},
"profile": {
"type": "string",
"description": "Confirmation profile that produced the proofs, e.g. 'attested' or 'signed'."
},
"proofs": {
"type": "object",
"description": "Proofs of authorization keyed by action; form defined by the confirmation profile.",
"properties": {
"proposal": { "type": "object" },
"confirmation": { "type": "object" },
"decline": { "type": "object" },
"withdrawal": { "type": "object" },
"revocation": { "type": "object" }
},
"additionalProperties": false
},
"description": { "type": "string" },
"created_at": {
"type": "string",
"format": "date-time",
"$comment": "format: date-time is annotation-only in JSON Schema 2020-12 (here and in updated_at); a deployment MAY choose to assert it."
},
"updated_at": { "type": "string", "format": "date-time" },
"ext": {
"type": "object",
"description": "Optional deployment- or substrate-specific extension fields (section 3); contents are unconstrained."
}
},
"additionalProperties": false,
"allOf": [
{
"if": {
"required": ["status"],
"properties": { "status": { "const": "proposed" } }
},
"then": {
"required": ["proofs"],
"properties": {
"proofs": {
"required": ["proposal"],
"not": { "anyOf": [
{ "required": ["confirmation"] },
{ "required": ["decline"] },
{ "required": ["withdrawal"] },
{ "required": ["revocation"] }
] }
}
}
}
},
{
"if": {
"required": ["status"],
"properties": { "status": { "const": "established" } }
},
"then": {
"required": ["proofs"],
"properties": {
"proofs": {
"required": ["proposal", "confirmation"],
"not": { "anyOf": [
{ "required": ["decline"] },
{ "required": ["withdrawal"] },
{ "required": ["revocation"] }
] }
}
}
}
},
{
"if": {
"required": ["status"],
"properties": { "status": { "const": "declined" } }
},
"then": {
"required": ["proofs"],
"properties": {
"proofs": {
"required": ["proposal", "decline"],
"not": { "anyOf": [
{ "required": ["confirmation"] },
{ "required": ["withdrawal"] },
{ "required": ["revocation"] }
] }
}
}
}
},
{
"if": {
"required": ["status"],
"properties": { "status": { "const": "withdrawn" } }
},
"then": {
"required": ["proofs"],
"properties": {
"proofs": {
"required": ["proposal", "withdrawal"],
"not": { "anyOf": [
{ "required": ["confirmation"] },
{ "required": ["decline"] },
{ "required": ["revocation"] }
] }
}
}
}
},
{
"if": {
"required": ["status"],
"properties": { "status": { "const": "revoked" } }
},
"then": {
"required": ["proofs"],
"properties": {
"proofs": {
"required": ["proposal", "confirmation", "revocation"],
"not": { "anyOf": [
{ "required": ["decline"] },
{ "required": ["withdrawal"] }
] }
}
}
}
}
]
}
¶
B.1: delegates_to, ERC-8004 substrate, attested
profile.¶
{
"id": "rel:8004:42",
"source": "11155111:123",
"target": "11155111:456",
"type": "delegates_to",
"status": "established",
"profile": "attested",
"proofs": {
"proposal": {
"controller": "0xA11ce...",
"attested_by": "attesting-service.example",
"method": "siwe",
"at": "2026-06-18T10:00:00Z"
},
"confirmation": {
"controller": "0xB0b...",
"attested_by": "attesting-service.example",
"method": "siwe",
"at": "2026-06-18T10:05:00Z"
}
},
"created_at": "2026-06-18T10:00:00Z",
"updated_at": "2026-06-18T10:05:00Z"
}
¶
B.2: depends_on, non-blockchain substrate (did:web),
signed profile. Demonstrates substrate-neutrality and the
trustless profile: each controller signs the record directly, verifiable
against its DID, and here each agent is its own controller (no human in
the loop).¶
{
"id": "rel:example:2",
"source": "did:web:alpha.example",
"target": "did:web:beta.example",
"type": "depends_on",
"status": "established",
"profile": "signed",
"proofs": {
"proposal": {
"controller": "did:web:alpha.example",
"verificationMethod": "did:web:alpha.example#key-1",
"signature": "z3Jx..."
},
"confirmation": {
"controller": "did:web:beta.example",
"verificationMethod": "did:web:beta.example#key-1",
"signature": "z58P..."
}
}
}
¶
B.3: delegates_to, ERC-8004 substrate, attested
profile, after revocation. A relationship that was established and then
revoked; the revocation proof attributes the action to the
revoking controller (here the target).¶
{
"id": "rel:8004:42",
"source": "11155111:123",
"target": "11155111:456",
"type": "delegates_to",
"status": "revoked",
"profile": "attested",
"proofs": {
"proposal": {
"controller": "0xA11ce...",
"attested_by": "attesting-service.example",
"method": "siwe",
"at": "2026-06-18T10:00:00Z"
},
"confirmation": {
"controller": "0xB0b...",
"attested_by": "attesting-service.example",
"method": "siwe",
"at": "2026-06-18T10:05:00Z"
},
"revocation": {
"controller": "0xB0b...",
"attested_by": "attesting-service.example",
"method": "siwe",
"at": "2026-06-19T09:00:00Z"
}
},
"created_at": "2026-06-18T10:00:00Z",
"updated_at": "2026-06-19T09:00:00Z"
}
¶