Draft — v1.0 — 90-day public comment window
Published 2026-07-26. Public-comment window closes 2026-10-24. The tri-state names (observed_clean / never_observed / degraded) and the freshness fields (valid_until, stale_if_error, key_id) are the contract; producers add sources without breaking consumers.
Read the raw markdown  ·  conformance vector verify-203.0.113.45.ndjson  ·  envelope Receipt Format v1.0

TunnelMind Signed Attribute Bundle v1.0

Status: DRAFT published 2026-07-26 · 90-day public-comment window closes 2026-10-24. License: CC BY 4.0. Canonical URL: https://tunnelmind.ai/standards/attribute-bundle/v1 Live producer: POST https://data.tunnelmind.ai/v1/verify/{node} (REST + NDJSON stream) Conformance vector: https://tunnelmind.ai/wp-mac/fixtures/verify-203.0.113.45.ndjson Companion: Receipt Format v1.0 — the signed envelope this bundle rides in. The bundle is the claim set; the receipt is the proof wrapper.

A Signed Attribute Bundle is the output shape of a Policy Information Point (PIP) for the agentic internet: per-source facts about a network node, each labeled with an explicit coverage state, plus a freshness contract and a signature binding — so a Policy Decision Point (PDP) can consume the facts without ever being asked to trust a verdict.

The design position: a PIP owes its PDP three guarantees no verdict score can carry —

  1. Coverage honesty — for every source, whether the PIP actually looked, and whether "no data" means clean, blind spot, or failure.
  2. Freshness contract — how long the facts are current, and what the consumer may do when a refresh fails.
  3. Verifiable provenance — a signature a relying party can check offline.

1. Coverage tri-state (normative)

Every per-source entry MUST carry exactly one of three states. No synonyms, no fourth state:

State Meaning
observed_clean The source looked and holds a real observation of this node.
never_observed The source has nothing to say — wrong node type for this source, or it looked and the node is not in its corpus. This is a correct answer, not a placeholder. An honest blind spot MUST NOT be dressed up as an observation or a failure.
degraded The source SHOULD have answered but errored, timed out, or is misconfigured — or returned an observation with no recency window where one is required. A producer MUST NOT invent a value to avoid this state.

Classification rules (normative):

2. Wire shape

2.1 The coverage block

{
  "coverage": {
    "lenses": [
      {
        "lens": "scry",                        // producer-defined source id
        "check": "exit-node observations",     // what this source EXAMINES — self-describing, locked string
        "state": "observed_clean",
        "summary": "3 obs · scanner",          // human-scale digest of the observation, null unless observed_clean
        "observed_at": "2026-07-24T11:02:00Z"  // per-node recency when the source has one, else null
      },
      {
        "lens": "tracker",
        "check": "demand-side graph",
        "state": "never_observed",
        "summary": null,
        "observed_at": null,
        "reason": "not_in_corpus"              // present when state != observed_clean
      }
      // ... one entry per source, fixed order, ALWAYS all sources — a consumer
      // must never have to infer a missing source's state from its absence.
    ],
    "rollup": { "observed_clean": 3, "never_observed": 1, "degraded": 0 },
    "valid_until": "2026-07-26T06:05:00Z",     // when a consumer should re-poll
    "stale_if_error": 86400,                   // seconds the consumer MAY keep using this bundle if a re-poll fails
    "key_id": "tm-2026-06"                     // which published signing key backs the receipt
  }
}

Freshness semantics (normative):

2.2 Streaming form (NDJSON)

Producers that stream results emit one JSON object per line. Per-source events carry the coverage state inline so a consumer can act before the bundle completes:

{"event":"lens_result","lens":"scry","coverage_state":"observed_clean","observed_value":"3 obs · scanner · Familiar SFO-2","observed_at":"2026-07-24T11:02:00Z"}
{"event":"lens_result","lens":"sigil","coverage_state":"observed_clean","observed_value":"1204 edges · 2 unauthorized","observed_at":null}
{"event":"verdict","coverage":{...full block as §2.1...},"sig":"ed25519:9f3a…c17b"}

Ordering is NOT guaranteed — events are keyed by lens, and a consumer MUST tolerate out-of-order arrival. The conformance vector linked above exercises this.

3. What this bundle is not

4. Consuming from a PDP

OPA/Rego example — gate on coverage instead of trusting a score:

package agentgate

import rego.v1

# Fetch the bundle (or use OPA's external-data replication with the same shape)
bundle := http.send({
    "method": "POST",
    "url": sprintf("https://data.tunnelmind.ai/v1/verify/%s", [input.node]),
    "timeout": "5s",
}).body.data

# Deny on any degraded source — the PIP itself told us it failed
deny contains msg if {
    some l in bundle.coverage.lenses
    l.state == "degraded"
    msg := sprintf("PIP degraded on %s (%s)", [l.lens, l.reason])
}

# Require the sources YOUR policy cares about to have actually observed the node
required := {"scry", "ghostroute"}
deny contains msg if {
    some name in required
    some l in bundle.coverage.lenses
    l.lens == name
    l.state == "never_observed"
    msg := sprintf("no observation for required source %s", [name])
}

# Respect the freshness contract
deny contains "bundle expired" if {
    time.parse_rfc3339_ns(bundle.coverage.valid_until) < time.now_ns()
}

5. Conformance

A producer conforms if:

  1. Every bundle carries all sources, each in exactly one tri-state, ordered and named consistently across responses.
  2. summary/observed_at are null outside observed_clean; reason present outside observed_clean.
  3. valid_until, stale_if_error, key_id are always present.
  4. The rollup counts equal the per-source states.
  5. Streamed events reconcile exactly with the final bundle (same states, keyed by source, order-independent).

The published vector (verify-203.0.113.45.ndjson, TEST-NET-3 documentation address) is a complete worked example: real check strings, out-of-order emission, and a signed bundle close. Verify receipts offline with @tunnelmindai/receipt-verify.

6. Comment

Issues and discussion: [email protected] — subject attribute-bundle. Window closes 2026-10-24.

Implementation status
The live producer is POST data.tunnelmind.ai/v1/verify/{node} (REST + NDJSON stream) — the coverage block ships on every verdict and is committed inside the signed receipt payload. Background: the PIP whitepaper.

Verify receipts offline with @tunnelmindai/receipt-verify (Apache-2.0).
Discuss via the standards hub at /standards. Public comment ends 2026-10-24.