---
title: "Direct Machine Interface (DMI): a discovery, policy and receipt profile for agent-reachable lines"
abbrev: "Qlaas DMI profile"
docname: draft-qlaas-dmi-profile-00
category: info
submissiontype: independent
ipr: trust200902
area: ""
workgroup: ""
keyword: [agents, discovery, MCP, A2A, receipts, authorization]
stand_alone: yes
pi: [toc, sortrefs, symrefs]
date: 2026-10-03
author:
  - org: Qlaas (Dyrhoff Ltd)
    email: hello@qlaas.co.uk
normative:
  RFC2119:
  RFC8174:
  RFC3339:
  RFC7517:
  RFC8037:
  RFC8032:
  RFC8615:
  RFC8785:
  RFC9421:
  RFC9460:
  RFC9727:
informative:
  RFC6973:
  RFC7638:
  RFC9309:
  RFC9530:
  RFC6962:
  AUTHZEN:
    title: "Authorization API 1.0"
    author: { org: "OpenID Foundation AuthZEN Working Group" }
  WBA:
    title: "Web Bot Auth: HTTP Message Signatures directory and protocol (Internet-Drafts)"
    author: { org: "IETF individual submissions (draft-meunier-*)" }
  DNSAID:
    title: "DNS for AI Discovery (DNS-AID)"
    author: { org: "DNS-AID project, Linux Foundation" }
  A2A:
    title: "Agent2Agent (A2A) Protocol"
    author: { org: "Linux Foundation A2A project" }
  MCP:
    title: "Model Context Protocol"
    author: { org: "Model Context Protocol project" }
---

--- abstract

This document profiles existing standards so that a person or business can publish one signed description of how
software agents may reach them, which requests their own rules allow, require a step-up for, or deny, and how each
decision is recorded. The description (a "DMI manifest") is served at a well-known URI and may be found
through DNS. It points to Model Context Protocol (MCP) and Agent2Agent (A2A) endpoints. It does not define a new
wire protocol. Decisions map onto the OpenID AuthZEN request and response shape, operator identity uses HTTP Message
Signatures (RFC 9421) as profiled by Web Bot Auth, and every decision is written to an Ed25519-signed, hash-chained
receipt over JCS (RFC 8785) canonical JSON.

--- middle

# Status of this document {#status}

This is an **individual draft**. It has not been adopted by any IETF working group or other standards body, and
nothing in it is an Internet Standard. It is published so that others can implement, test and criticise it. As of
this draft's date, the proposed IETF DAWN working group was not chartered (July 2026), and DNS-AID moved to the
Linux Foundation (May 2026); this profile follows DNS-AID's naming convention but does not depend on its eventual
form. No adoption, endorsement or conformance by any third party is claimed.

One reference implementation exists: the Qlaas DMI line server (`UIE-founder/qlaas-dmi`, branch `feat/next-phase`,
`src/dmi/manifest.ts`, `src/agentline/*`). Where this document and that code disagree on the wire format, the
disagreement is a defect in this document and will be corrected in the next revision. Sections marked
**(not implemented)** describe behaviour the reference implementation does not yet have.

Licence. The text of this draft is intended for submission under the IETF Trust Legal Provisions (BCP 78 and
BCP 79, `trust200902`). The JSON Schemas, test vectors and code that accompany it
(`https://qlaas.co.uk/spec`) are licensed under the Apache License, Version 2.0.

# Introduction

An agent that wants something from a person today often reaches them through interfaces built for people: a phone
call, a voice greeting, a web form. A DMI line instead publishes, in one signed document:

- what it is and who it represents (the *resource*);
- what an agent can ask for (the *capabilities*), with an input contract, constraints and consequences;
- the owner's standing policy for each capability (ALLOW, STEP_UP or DENY), and who decides when a request steps up;
- the ranked ways to reach it (the *execution paths*), machine paths first;
- how to check the evidence it will produce (the *receipt key*).

The manifest describes; it does not grant. Being listed never authorises anything: every request is evaluated
against the owner's rules at the time it arrives, and the evaluation is receipted.

## Terminology

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.

Line:
: An origin (scheme, host, port) that answers for one principal and serves a DMI manifest.

Principal / owner:
: The person or business a line represents. The owner sets the line's rules.

Mandate:
: The owner's explicit, scoped, expiring set of rules that decides each capability request. Anything not granted is
  denied.

Caller tier:
: The evidence the line holds about who is calling: `declared` (self-described), `verified-intermediary` (the agent
  platform proved its identity, the end user did not), `verified` (an OAuth grant the owner approved, or a key the
  owner trusts), plus `observed` and `inferred` for non-agent signals.

Receipt:
: A signed record of one decision, linked to the previous receipt by hash.

# Overview

~~~
 agent                          DNS                         line (https://line.example)
   |  _dmi._mcp._agents.example.com SVCB? ---------------->|          (or legacy _qlaas TXT)
   |  GET /.well-known/dmi.json ------------------------------------------------------------->|
   |<---------------------------------------- signed manifest (capabilities, paths, policy) ---|
   |  verify signature (JCS + Ed25519) against evidence.receipts.key, bind key to origin       |
   |  MCP tools/call or A2A message/send  (+ RFC 9421 Web Bot Auth signature, optional)  ----->|
   |                                            mandate evaluates: ALLOW | STEP_UP | DENY      |
   |<------------------------------- task state + receipt id(s); receipt signed and chained ---|
~~~

# The manifest {#manifest}

## Encoding {#encoding}

A manifest is a single I-JSON object served with media type `application/json` at
`/.well-known/dmi.json` on the line's origin {{RFC8615}}. Consumers MUST ignore members they do not
understand. Producers MUST NOT rely on member order. The machine-readable schema is
`https://qlaas.co.uk/spec/dmi-manifest-0.2.schema.json` (JSON Schema 2020-12, using only `type`, `required`,
`properties`, `items`, `enum`, `const`, `pattern`, `minLength`, `minimum`, `minItems`, `format` and local `$ref`).

## Profile identifier and versioning {#profile}

The `dmi` member (REQUIRED) names the wire profile. This revision defines exactly one value:
`worldauth.dmi.communications/0.1`. A consumer that does not recognise the value MUST NOT act on the manifest's
policy claims and SHOULD treat the line as having no DMI manifest.

"0.2" is the revision of the *schema* describing that profile, not a new wire identifier. Schema revision 0.1 was an
informal minimal example. A change that would make a currently valid manifest invalid will use a new profile
identifier.

`generatedAt` (REQUIRED) is an RFC 3339 {{RFC3339}} date-time. A consumer SHOULD treat a manifest whose
`generatedAt` is more than five minutes in the future as suspect (clock fault or replay of a forged document).

## Members {#members}

The table lists every member the reference implementation emits today. REQUIRED members are those a
conforming manifest MUST contain; OPTIONAL members MAY be omitted.

| Member | Req. | Content |
|---|---|---|
| `dmi` | REQUIRED | Profile identifier ({{profile}}). |
| `generatedAt` | REQUIRED | RFC 3339 date-time the document was produced. |
| `resource` | REQUIRED | Object: `kind` (REQUIRED, `<noun>.<noun>`, e.g. `person.line`, `business.line`), `principal` (REQUIRED, `name` REQUIRED, `handle` OPTIONAL), `line` (REQUIRED, the line's origin URI), `identity`, `agentCard` (OPTIONAL URIs), `addresses` (OPTIONAL: `door` URI, `federated` `handle@host`, `tel` `tel:` URI). |
| `state` | OPTIONAL | Coarse current state: `asOf` and `evidence` (`observed` or `declared`) REQUIRED when present; `acceptingCalls`, `ownerReachableNow` (booleans), `quietUntil` (date-time), `preferredForMachines`. A line MAY publish only `asOf`, `evidence: "declared"` and `preferredForMachines`, and SHOULD do so when the owner has not chosen to expose presence. State never reveals location or reason. |
| `capabilities` | REQUIRED | Non-empty array ({{capabilities}}). |
| `executionPaths` | REQUIRED | Non-empty array ({{paths}}). |
| `contact` | OPTIONAL | `mode` (`full` or `message_first`), `humans` (`allow`, `screen`, `message_only`), `headline`. |
| `upgrade` | OPTIONAL | How a caller who reached the line by voice continues on a structured path (`field`, e.g. `metadata.upgradeCode`). |
| `evidence` | REQUIRED | `receipts` (REQUIRED, {{receipt-key}}) and `disclosure` (OPTIONAL text: how the line discloses that an assistant is an AI). |
| `discovery` | OPTIONAL | `wellKnown` (URIs), `dns` and `dnsAliases` (record text). Informative only: a consumer MUST NOT follow these to another origin without applying {{binding}}. |
| `signatures` | REQUIRED | Non-empty array ({{manifest-signature}}). |

## Capabilities {#capabilities}

Each capability object has `id` (REQUIRED, `^[a-z][a-z0-9_]{0,63}$`) and `paths` (REQUIRED, non-empty array of
execution path ids). Every entry of `paths` MUST equal the `id` of an entry in `executionPaths`. OPTIONAL members:
`name`, `description`, `contract` (`input`: a JSON Schema for the arguments; `output`: a description of the result
and its evidence), `constraints` (strings), `consequences` (what happens in the world if the request is
carried out), `authority` ({{authority}}), `outcome` (the possible task outcomes) and `evidence` (what record
results).

The reference line publishes `leave_message`, `request_callback`, `check_availability`, `propose_meeting` and
`urgent_patch_through` on the MCP and A2A paths, plus `message_owner` and (when the line takes live
calls) `voice_call` on paths for people. The capability set is not fixed by this profile.

## Published authority {#authority}

`authority` (OPTIONAL) states the owner's standing policy for a capability so that an agent can predict the outcome
before calling:

- `default` (REQUIRED when `authority` is present): `ALLOW`, `STEP_UP` or `DENY`.
- `rules` (OPTIONAL): ordered array of `{effect, when?}`. `when` MAY contain `tiers` (caller tiers), `vendors`
  (operator identifiers) and `maxMinutes`. The first rule whose `when` matches decides.
- `note` (OPTIONAL): text.

Published authority is advisory. The line MUST evaluate each request at the time it arrives, and MAY reach a
stricter decision than published (for example a step-up outside free time, an owner override, a quiet period or a
rate limit). A line MUST NOT reach a *more permissive* decision than its published rules imply for the caller's
tier. Unknown capabilities are denied.

## Execution paths {#paths}

Each path object has `rank` (REQUIRED, integer ≥ 1, unique within the manifest; lower is preferred), `id`
(REQUIRED, `^[a-z][a-z0-9-]{0,63}$`), `protocol` (REQUIRED, human-readable) and `for` (REQUIRED, `machines` or
`people`). OPTIONAL: `url`, `auth` (array of accepted schemes, each with `scheme` and `tier`), `cdo` (declared
cost-of-delivery estimates: protocol hops, model invocations on the line, human interventions, typical latency),
`evidence` (`declared` or `observed`), `secondary` (boolean) and `note`.

Machine paths SHOULD come first and SHOULD use `https`. The reference line ranks MCP (streamable HTTP) 1 and A2A
(JSON-RPC) 2. `cdo` values are design estimates, not measurements, unless `evidence` is `observed`.

Accepted `auth` schemes in the reference line:

| `scheme` | `tier` granted | Notes |
|---|---|---|
| `oauth2` | `verified` | OAuth 2.1 grant the owner approved; metadata at `/.well-known/oauth-authorization-server`. |
| `web-bot-auth` | `verified-intermediary` | {{operator}}. |
| `qlaas-agent-signature` | `verified` if the owner trusts the key | Legacy, **not** RFC 9421: Ed25519 over `` `${Signature-Created}.${rawBody}` ``. NOT RECOMMENDED for new implementations. |
| `none` | `declared` | Anonymous agents may leave messages; consequential actions step up. |

## Receipt key {#receipt-key}

`evidence.receipts` (REQUIRED) contains `alg` (REQUIRED, `EdDSA`), `canonicalization` (REQUIRED, `JCS`),
`keyId` (REQUIRED), `key` (REQUIRED, an Ed25519 public JWK {{RFC7517}} {{RFC8037}}: `kty` `OKP`, `crv`
`Ed25519`, 43-character base64url `x`), and OPTIONAL `chained` (boolean), `jwks` (URI of a JWK Set holding the same
key, conventionally `/.well-known/qlaas/receipt-key.json`), `jwksAliases` and `versions` (receipt format
identifiers the line emits).

The reference line derives `keyId` as `pbk-` followed by the first 16 characters of the base64url SHA-256 of the
key's SubjectPublicKeyInfo DER. Consumers MUST treat `keyId` as opaque.

## Manifest signature {#manifest-signature}

`signatures` (REQUIRED) is an array of `{keyId, alg: "EdDSA", canonicalization: "JCS", signature}`. The signing
input is the JCS {{RFC8785}} serialisation of the manifest object **with the `signatures` member removed**;
`signature` is the 64-byte Ed25519 {{RFC8032}} signature, base64url without padding (86 characters).

A conforming manifest MUST carry a signature whose `keyId` equals `evidence.receipts.keyId` and which verifies with
`evidence.receipts.key`. Further signatures (for example by an operator of a hosting platform) MAY be added; each
covers the same input, so signatures are independent of each other.

## Binding the key to the line {#binding}

A signature by a key the manifest itself declares proves only that the document is intact since a holder of that key
signed it. It does not prove who holds the key. A consumer MUST bind the key to the line by fetching the manifest
over HTTPS, with normal certificate validation, from the line's own origin (`resource.line`) at
`/.well-known/dmi.json`. It SHOULD strengthen that binding by either:

1. confirming that `evidence.receipts.jwks`, fetched from the same origin, contains the same key; or
2. a DNS record validated with DNSSEC ({{discovery}}) naming the line.

A manifest obtained any other way (a cache, a directory, a message) MUST NOT be trusted further than the key binding
established for it. Key pinning across fetches (TOFU) is RECOMMENDED for repeat callers, with a key change treated as
a reason to re-establish binding, not as an error.

# Discovery {#discovery}

A consumer that starts from a domain name `example.com` SHOULD try, in order:

## Well-known URI

`GET https://example.com/.well-known/dmi.json`. A line that is its own domain serves the manifest there. Redirects
to another origin MAY be followed; the key binding ({{binding}}) is then to the final origin.

## DNS-AID SVCB mapping (not implemented)

Following the DNS-AID naming convention {{DNSAID}}, a domain MAY publish an SVCB record {{RFC9460}} at
`_dmi._mcp._agents.<domain>` whose TargetName is the line's host:

~~~
_dmi._mcp._agents.example.com. 3600 IN SVCB 1 line.example.com. (
    alpn="h2,h3" port=443 key65400="/.well-known/dmi.json" )
~~~

`key65400` is in the SvcParamKey private-use range and carries the manifest path; it will be replaced by the key
requested in {{iana-svcb}} if that is registered. The `_mcp` label signals that the line's preferred machine path is
MCP; `_dmi._a2a._agents` MAY be published alongside. A consumer MUST still fetch the manifest over HTTPS and apply
{{binding}}; with DNSSEC validation the record also contributes to the binding.

The reference line does not publish SVCB records today, and the DNS-AID naming may change; this section will track
it.

## Legacy TXT record

`_qlaas.<domain>` TXT `"v=qlaas1; line=<origin>; handle=<handle>"` (and the earlier `_playbakk.<domain>` TXT
`"v=pbk1; …"`). `line` is REQUIRED and MUST be an `https` (or, for local testing only, `http`) URI; only its
origin is used. `handle` is OPTIONAL (`^[a-z0-9_.-]{1,64}$`). Unknown keys are ignored. This is what the reference
line's `discoverLine()` reads today. New deployments SHOULD publish the well-known URI and MAY publish the
SVCB record; the TXT form is retained for compatibility.

## Relationship to other catalogues

A line SHOULD also publish an A2A agent card {{A2A}} at `/.well-known/agent-card.json` and MAY publish an MCP server
card {{MCP}}; their endpoints MUST be the ones listed in `executionPaths`. A site that lists APIs in an RFC 9727
`/.well-known/api-catalog` {{RFC9727}} MAY add a link to `/.well-known/dmi.json`. Where these documents disagree,
the signed DMI manifest describes the owner's policy; the protocol-specific documents describe the protocol.

# Policy decisions and OpenID AuthZEN {#authzen}

A line decides every capability request with one of three effects. This section maps them onto the OpenID AuthZEN
Authorization API {{AUTHZEN}} evaluation shape so that policy engines and audit tools can consume them without a DMI-
specific model. The reference line evaluates internally and does **not** expose an AuthZEN endpoint
**(not implemented)**; the mapping below is normative for any line that does.

Request (one capability call):

~~~ json
{
  "subject":  { "type": "agent", "id": "<counterparty id: key id, OAuth client, or declared label>",
                "properties": { "tier": "declared", "vendor": "<operator id, if verified>" } },
  "action":   { "name": "agentline.propose_meeting",
                "properties": { "args": { "start": "2026-09-22T10:00:00+01:00", "durationMin": 30 } } },
  "resource": { "type": "person.line", "id": "https://line.example" },
  "context":  { "mandate": "mandate-default", "time": "2026-09-21T14:15:00Z" }
}
~~~

Response:

| DMI effect | AuthZEN `decision` | `context` |
|---|---|---|
| `ALLOW` | `true` | `{"dmi_effect":"ALLOW","receipt":"<receipt id>"}` |
| `STEP_UP` | `false` | `{"dmi_effect":"STEP_UP","reason_user":{…},"receipt":"<receipt id>"}`; the request waits for the owner (A2A `input-required`). A later owner decision produces a new receipt. |
| `DENY` | `false` | `{"dmi_effect":"DENY","reason_admin":{…},"receipt":"<receipt id>"}` |

`STEP_UP` maps to `false` because AuthZEN decisions are boolean and the requested action has not been permitted at
the time of the response. A client MUST NOT treat `STEP_UP` as a refusal to retry elsewhere: it means "the owner
decides". The receipt's `decision` member always carries the three-valued effect.

Ordering. Argument validation precedes the authority decision; invalid arguments are `DENY`. The mandate is
evaluated first-match; then owner controls (overrides, quiet periods, contact lanes) and rate limits MAY make the
decision stricter, never more permissive ({{authority}}).

# Receipts {#receipts}

## Format

A receipt is `{payload, signature, keyId}` (schema: `https://qlaas.co.uk/spec/dmi-receipt-0.1.schema.json`).

| `payload` member | Req. | Content |
|---|---|---|
| `v` | REQUIRED | `qlaas.receipt/0.1` (the reference line also accepts the identical earlier `playbakk.receipt/0.1`). |
| `id` | REQUIRED | `rcpt_` + up to 64 base64url characters, unique per line. |
| `at` | REQUIRED | RFC 3339 date-time. |
| `prev` | REQUIRED | `null` for the first receipt of a line, else the chain link below. |
| `actor` | REQUIRED | `{kind: agent | owner | system, id}`: who made the decision (the owner's agent, the owner, or the system). |
| `counterparty` | OPTIONAL | `{id, kind: human | agent | business, tier}`. |
| `action` | REQUIRED | Dotted name, e.g. `agentline.leave_message`, `contact.message`, `policy.update`. |
| `decision` | REQUIRED | `ALLOW`, `STEP_UP` or `DENY`. |
| `subject` | OPTIONAL | Object: task id, arguments, call id. |
| `reasons` | REQUIRED | Array of strings, possibly empty. |

`signature` is Ed25519 over JCS(`payload`), base64url (86 characters). `keyId` names the line's receipt key
({{receipt-key}}).

## Hash chain

`payload.prev` of receipt *n* is base64url(SHA-256(JCS(receipt *n−1*))), where the hash covers the **whole signed
receipt** (`payload`, `signature` and `keyId`), without padding. A verifier holding receipts *i..j* and the hash of
receipt *i−1* can verify that window. Removing, inserting or reordering a receipt breaks a link; altering a payload
breaks its signature.

A chain proves order and integrity *within what the verifier was shown*. It does not prove completeness: a line
could withhold its newest receipts, or keep two chains. Receipts stay with the owner; there is no central log.

## Inclusion proofs (future)

A later revision is expected to let a line periodically publish a signed tree head over its receipt hashes, so that a
counterparty given one receipt can obtain a Merkle inclusion proof (in the style of {{RFC6962}}) without seeing other
receipts, and so that independent witnesses can detect split views. **Not implemented**; no format is defined here.

# Operator identity {#operator}

An agent platform proves which platform sent a request with HTTP Message Signatures {{RFC9421}} as profiled by Web
Bot Auth {{WBA}}. A line that accepts it:

- requires `Signature-Input`, `Signature` and `Signature-Agent`, with `tag="web-bot-auth"`, `created` and `expires`,
  and `@authority` covered and equal to the line's own host;
- requires `content-digest` {{RFC9530}} to be covered for a request with a body, and checks it;
- fetches the key from `<Signature-Agent origin>/.well-known/http-message-signatures-directory` only for origins on
  its allowlist, never for unknown ones, and matches `keyid` to the JWK thumbprint {{RFC7638}} or `kid`;
- accepts each nonce (or signature) once until it expires;
- assigns the tier `verified-intermediary`.

A valid signature proves the platform, not the person it acts for. A line MUST NOT treat `verified-intermediary` as
`verified`. The owner's mandate MAY name operators in `when.vendors`.

# Conformance {#conformance}

A manifest conforms to this profile when all MUST clauses below hold. The Qlaas Agent-Ready Check
(`https://qlaas.co.uk/check`) reports each clause for a domain's `/.well-known/dmi.json`; it does not change the
Check's score, and passing does not imply anything about the line's behaviour at run time.

| Clause | Level | Test |
|---|---|---|
| `json` | MUST | The document is a JSON object ({{encoding}}). |
| `profile` | MUST | `dmi` is a recognised identifier ({{profile}}). |
| `schema` | MUST | Valid against `dmi-manifest-0.2.schema.json` ({{members}}). |
| `ranks` | MUST | Execution path ranks are distinct integers ≥ 1 ({{paths}}). |
| `references` | MUST | Every capability path names a listed execution path ({{capabilities}}). |
| `signature` | MUST | A JCS + Ed25519 signature under `evidence.receipts.keyId` verifies ({{manifest-signature}}). |
| `https` | SHOULD | Machine paths with a URL use `https` ({{security}}). |
| `clock` | SHOULD | `generatedAt` is not more than five minutes in the future ({{profile}}). |

Test vectors (`https://qlaas.co.uk/spec/vectors/`) include a valid manifest in two modes, ten invalid manifests each
naming exactly the clauses it fails, a valid three-receipt chain and five broken chains. **All vectors are signed
with the public RFC 8032 section 7.1 test keys and are for testing only.**

# Security considerations {#security}

Self-asserted keys. A manifest's signature proves integrity, not identity ({{binding}}). The origin, over HTTPS, is
the root of trust in this revision.

Description is not authority. A manifest MUST NOT be read as a grant. Agents MUST expect STEP_UP and DENY for listed
capabilities, and lines MUST evaluate each request at arrival.

Replay and freshness. Manifests carry `generatedAt` but no expiry; consumers SHOULD re-fetch rather than cache for
long, and SHOULD honour HTTP caching headers. Request signatures are single-use ({{operator}}).

Prompt injection. `description`, `consequences`, `note` and similar members are text written by the line's owner.
An agent MUST treat them as data, not instructions.

Downgrade. A line that lists `none` among its `auth` schemes accepts anonymous callers at the `declared` tier;
published authority shows what that tier can do. Consumers MUST NOT infer a higher tier from the presence of
stronger schemes.

Key compromise. The receipt key signs both the manifest and receipts. A compromised key can forge both from the time
of compromise. Rotation (publish the new key in the manifest and JWKS, start a new chain whose first receipt
records the rotation) is RECOMMENDED; a rotation format is not yet defined.

Canonicalisation. Producers and verifiers MUST use JCS exactly; numbers outside the I-JSON range or non-finite numbers
MUST NOT appear in signed objects.

Transport. Machine paths SHOULD use HTTPS; plain HTTP exposes both arguments and bearer tokens.

# Privacy considerations {#privacy}

The manifest is public. It names a principal and, if the owner chooses, coarse presence (`acceptingCalls`,
`ownerReachableNow`, `quietUntil`). Lines SHOULD default to not exposing presence ({{RFC6973}}), and MUST NOT publish
location, calendar contents, contact details or the reason a person is unavailable. `check_availability`
returns free slots only.

Receipts contain request arguments and counterparty identifiers. They are held by the owner and disclosed only
by the owner's choice; a line MUST NOT publish receipts by default. Publishing tree heads (future) reveals receipt
counts over time; that leak is accepted only with the owner's consent.

Agent disclosure. A line whose assistant converses with people MUST disclose that it is an AI before anything else
(`evidence.disclosure`).

# IANA considerations {#iana}

This individual draft requests the following. None has been registered; until registered, implementations use the
values shown.

## Well-known URI

Registration in the "Well-Known URIs" registry {{RFC8615}}:

- URI suffix: `dmi.json`
- Change controller: the authors (to be transferred to the IETF if the document is adopted)
- Specification document: this document, {{manifest}}
- Status: provisional
- Related information: also used: `qlaas/receipt-key.json` (JWK Set of the receipt key); requested as a separate
  provisional entry `qlaas` (a namespace for line metadata).

## SVCB SvcParamKey {#iana-svcb}

Registration in the "Service Parameter Keys (SvcParamKeys)" registry {{RFC9460}}:

- Name: `dmi`
- Meaning: path of the DMI manifest on the target origin
- Format: a URI path beginning with `/`, presentation format as `dohpath` without the template
- Change controller: IETF
- Until assigned, the private-use key `key65400` is used ({{discovery}}).

## Media type (deferred)

A media type such as `application/dmi+json` may be requested in a later revision. This revision uses
`application/json`.

--- back

# Console to host provisioning profile (informative) {#provisioning}

This appendix records how the Qlaas console provisions a hosted line, as one example of operating DMI lines at scale.
It is not part of the profile and other operators need not follow it. The canonical contract is
`docs/CONSOLE_PROVISIONING.md` in `UIE-founder/qlaas-dmi`.

- One origin per line (`https://<handle>.dmi.qlaas.co.uk`), because cookies, passkey relying-party IDs, OAuth issuers
  and browser storage are per origin. Handles are DNS labels.
- The console holds entitlement (who paid); the host's registry holds operational state. On disagreement the
  console wins, through an idempotent reconcile keyed by the purchase id.
- Every console→host call (`POST /host/lines`, suspend, resume, sign-in, delete) is signed with the console's Ed25519
  key: headers `x-qlaas-host-timestamp` (±300 s), `x-qlaas-host-nonce` (single use) and
  `x-qlaas-host-signature: k1=<kid>:<base64url signature>` over a canonical string of timestamp, nonce, method,
  path and query, and the body's SHA-256. The host verifies against the console's JWK Set at
  `https://console.qlaas.co.uk/.well-known/qlaas-host-keys.json`. A transitional HMAC item (`v1=`) is sent only while
  a shared secret is still configured.
- The signing seed is generated inside the console's database vault and never shown to a person. It is decrypted
  into server memory to sign; it is not isolated in an HSM or KMS.

Status: tested end to end over loopback (a real host process spawning real line processes, a PGlite console
database); not yet run on a production host.

# Acknowledgements

None yet.
