Skip to content

IDProva launches April 7 — Registry packages coming at launch. Build from source now.

DAT Structure & Scopes

A Delegation Attestation Token is a JSON Web Signature (JWS) in Compact Serialization format (RFC 7515):

BASE64URL(Header) "." BASE64URL(Payload) "." BASE64URL(Signature)

For hybrid signatures, a fourth segment carries the ML-DSA-65 signature:

BASE64URL(Header) "." BASE64URL(Payload) "." BASE64URL(Ed25519Sig) "." BASE64URL(MLDSA65Sig)

This four-segment format is an IDProva extension to JWS. Implementations that do not support ML-DSA-65 MAY validate only the first three segments as standard JWS, but MUST note the reduced assurance level.

{
"alg": "EdDSA",
"typ": "idprova-dat+jwt",
"kid": "did:idprova:example.com:pratyush#key-ed25519-1",
"pqalg": "MLDSA65",
"pqkid": "did:idprova:example.com:pratyush#key-mldsa65-1"
}
FieldRequiredDescription
algYesMUST be EdDSA for Ed25519 signatures.
typYesMUST be idprova-dat+jwt.
kidYesDID URL of the signing key (Ed25519).
pqalgNoPost-quantum algorithm. MUST be MLDSA65 when present.
pqkidNoDID URL of the post-quantum signing key.

The DAT payload contains JWT-compatible claims:

{
"iss": "did:idprova:example.com:pratyush",
"sub": "did:idprova:example.com:kai-lead-agent",
"aud": "did:idprova:example.com:target-service",
"iat": 1708732800,
"exp": 1708819200,
"nbf": 1708732800,
"jti": "dat_01HQ3N8KXBC7YG2DMPVS5F6E9T",
"scope": [
"mcp:tool:filesystem:read",
"mcp:tool:filesystem:write",
"mcp:resource:context:read",
"idprova:agent:create"
],
"constraints": {
"maxCallsPerHour": 1000,
"allowedIPs": ["10.0.0.0/8", "172.16.0.0/12"],
"requiredTrustLevel": "L1",
"maxDelegationDepth": 2,
"geofence": ["AU", "NZ"]
},
"configAttestation": "blake3:a1b2c3d4e5f67890...",
"delegationChain": [
"dat_01HQ3M7JRAB6WF1CNKTS4E5D8S"
]
}
ClaimTypeRequiredDescription
issDID stringYesThe DID of the entity issuing (delegating) the token.
subDID stringYesThe DID of the entity receiving the delegation.
audDID stringNoThe intended verifier. When present, the verifier MUST check that its own DID matches.
iatNumericDateYesIssued-at timestamp (seconds since Unix epoch).
expNumericDateYesExpiration timestamp. Max lifetime: 24 hours for L0–L1; 7 days for L2+.
nbfNumericDateNoNot-before timestamp. Verifiers MUST reject the token before this time.
jtistringYesUnique token identifier. Format: dat_ + ULID or UUIDv7.
scopearrayYesArray of scope strings defining permitted actions.
constraintsobjectNoAdditional constraints on the delegation.
configAttestationstringNoExpected config hash of the subject agent. Mismatch → reject.
delegationChainarrayNoOrdered jti values forming the chain from root principal.

Scopes define what actions a delegated agent is permitted to perform.

scope = namespace ":" resource ":" action
namespace = segment
resource = segment *( ":" segment )
action = segment / wildcard
segment = 1*( ALPHA / DIGIT / "-" / "_" )
wildcard = "*"
NamespaceDescription
mcpModel Context Protocol operations
idprovaIDProva protocol operations
a2aAgent-to-Agent protocol operations
httpHTTP endpoint access
customUser-defined operations
ScopeDescription
mcp:tool:*:*All tool operations
mcp:tool:{name}:callCall a specific tool
mcp:tool:{name}:readRead tool metadata
mcp:resource:*:*All resource operations
mcp:resource:{name}:readRead a specific resource
mcp:resource:{name}:writeWrite a specific resource
mcp:prompt:*:*All prompt operations
mcp:prompt:{name}:useUse a specific prompt
ScopeDescription
idprova:agent:createCreate new agent identities
idprova:agent:updateUpdate agent identity documents
idprova:agent:deactivateDeactivate agent identities
idprova:delegation:issueIssue new DATs
idprova:delegation:revokeRevoke existing DATs
idprova:receipt:createCreate action receipts
idprova:receipt:readRead action receipts
  • * as the action component matches any action: mcp:tool:filesystem:* grants read, write, delete, etc.
  • * as a resource segment matches any resource: mcp:tool:*:read grants read on all tools.
  • *:*:* grants all permissions. MUST only be issued by L3+ principals and SHOULD be avoided.

When re-delegating, the child DAT’s scopes MUST be a subset of the parent DAT’s scopes. A delegatee MUST NOT escalate privileges.

Scope p covers scope s if:

  1. p equals s, OR
  2. p has a wildcard that matches the corresponding segment in s.
Parent scope: mcp:tool:filesystem:*
Child scope: mcp:tool:filesystem:read → VALID (covered by wildcard)
Child scope: mcp:tool:database:read → INVALID (different resource)

The constraints object provides restrictions beyond scopes:

{
"constraints": {
"maxCallsPerHour": 1000,
"maxCallsPerDay": 10000,
"maxConcurrent": 5,
"allowedIPs": ["10.0.0.0/8"],
"deniedIPs": ["10.0.0.1/32"],
"requiredTrustLevel": "L1",
"maxDelegationDepth": 2,
"geofence": ["AU", "NZ", "US"],
"timeWindows": [
{
"days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"startUTC": "00:00",
"endUTC": "23:59"
}
],
"requiredConfigAttestation": true,
"customConstraints": {
"maxTokensPerRequest": 4096,
"allowedModels": ["anthropic/claude-sonnet-4", "anthropic/claude-opus-4"]
}
}
}
FieldTypeDescription
maxCallsPerHourintegerMaximum actions per clock hour.
maxCallsPerDayintegerMaximum actions per calendar day (UTC).
maxConcurrentintegerMaximum concurrent active operations.
allowedIPsstring[]CIDR ranges from which the agent may operate.
deniedIPsstring[]CIDR ranges explicitly blocked. Overrides allowedIPs.
requiredTrustLevelstringMinimum trust level the subject must maintain.
maxDelegationDepthintegerMaximum further delegation depth. 0 = no re-delegation.
geofencestring[]ISO 3166-1 alpha-2 country codes.
timeWindowsobject[]Time windows during which the delegation is active.
requiredConfigAttestationbooleanIf true, verifiers MUST check config attestation.
customConstraintsobjectImplementation-specific constraints.

When re-delegating, child constraints MUST be equal to or more restrictive than parent constraints:

  • Numeric limits: child value ≤ parent value
  • IP ranges: child set ⊆ parent set
  • Trust level: child level ≥ parent level
  • Delegation depth: child depth < parent depth
  • Geofence: child set ⊆ parent set
  • Time windows: child windows ⊆ parent windows

Delegation chains trace the path of authority from a root principal to a leaf agent.

Root Principal (Human)
└── DAT_1: delegates to Agent A
└── DAT_2: Agent A delegates to Agent B
└── DAT_3: Agent B delegates to Agent C

The delegationChain array in DAT_3 would be: ["dat_1_jti", "dat_2_jti"].

ValidateChain(dat, resolver):
1. current = dat
2. chain = [current]
3. while current.delegationChain is not empty:
a. parent_jti = current.delegationChain[last]
b. parent_dat = resolver.resolveDAT(parent_jti)
c. if parent_dat is null: return INVALID("broken chain")
d. if parent_dat.sub != current.iss: return INVALID("chain mismatch")
e. if parent_dat.exp < now(): return INVALID("parent expired")
f. if not ValidateScopes(current.scope, parent_dat.scope):
return INVALID("scope escalation")
g. if not ValidateConstraints(current.constraints, parent_dat.constraints):
return INVALID("constraint escalation")
h. chain.prepend(parent_dat)
i. current = parent_dat
4. root = chain[0]
5. root_did = resolver.resolveDID(root.iss)
6. if root_did is null: return INVALID("unknown root")
7. if root_did.deactivated: return INVALID("deactivated root")
8. return VALID(chain)

Maximum Chain Depth: Default is 5. Restricted via maxDelegationDepth. Implementations MUST reject chains exceeding the maximum.


DATs can be revoked before expiry through two mechanisms.

The issuer publishes a revocation list at their DID’s service endpoint:

{
"issuer": "did:idprova:example.com:pratyush",
"updated": "2026-02-24T12:00:00Z",
"revocations": [
{
"jti": "dat_01HQ3N8KXBC7YG2DMPVS5F6E9T",
"revokedAt": "2026-02-24T11:30:00Z",
"reason": "key-compromise"
}
]
}
ReasonDescription
key-compromiseThe signing key has been compromised.
privilege-changeThe agent’s permissions have changed.
agent-deactivatedThe subject agent has been deactivated.
policy-violationThe agent violated its delegation policy.
supersededA new DAT has replaced this one.
unspecifiedNo specific reason given.

For real-time revocation, verifiers query the issuer’s registry:

GET /v1/delegations/{jti}/status
{
"jti": "dat_01HQ3N8KXBC7YG2DMPVS5F6E9T",
"active": false,
"revokedAt": "2026-02-24T11:30:00Z",
"reason": "privilege-change"
}

When a parent DAT in a chain is revoked, all child DATs in that chain are implicitly revoked. Verifiers MUST check the validity of the entire chain, not just the leaf DAT.