Skip to content

Guided flow · ECZ-ID IoT Device Passport™

IoT guided flow

Carry a connected device through the Accountability Chain: its identity, its controller, authority, current state, a proposed command, the physical consequence boundary, the result, and the Receipt and re-verify.

  1. Device identity
  2. Controller
  3. Authority
  4. Current state
  5. Proposed command
  6. Consequence boundary
  7. Result
  8. Receipt and re-verify

Steps at a glance · what is live today

  1. 01Identify the deviceLive · public
  2. 02Connect the controllerLive · public
  3. 03Establish authorityLive · signed in
  4. 04Read current stateLive · public
  5. 05Propose the commandLive · signed in
  6. 06Respect the physical consequence boundaryRelease-gated
  7. 07Report the result, as a reportYour system
  8. 08Receipt and re-verifyNo public retrieval

Step 01 of 8

Identify the device#

Identity

Live · public

Live in production and publicly readable, with no account and no key.

GoalGive each physical device a free ECZ-ID IoT Device Passport and label it with a QR code that routes to its live record.

Why it mattersOne device is one Passport; a fleet is many. Anyone standing next to the device can reach its live record without an app or an account.

Prerequisites

  • A device you operate.
  • Its serial number or a device attestation reference.

Example

yamlIllustrative fleet manifest entry and label. Use the values your TrustOps console shows.
devices:
  - serial: SN-0042-7781
    ecz_id: ECZ-GB-EXAMPL::IOT_DEVICE-AB12CD

# device label QR encodes the live record:
# https://resolver.ecocitizenz.org/p/ECZ-GB-EXAMPL::IOT_DEVICE-AB12CD

The QR code only routes to the live record. It is not proof by itself, and a printed label never shows current state.

What this proves

  • That the device has a persistent, publicly resolvable identity under its operator, once issued.

What this does not prove

  • That the device in front of you is the one the label names. A label can be copied.
  • That the device is safe or functioning.

Next: Connect the controller →

Step 02 of 8

Connect the controller#

IdentityAuthority

Live · public

Live in production and publicly readable, with no account and no key.

GoalResolve the Business Passport of the operating organisation that answers for the device.

Why it mattersEvery child Passport resolves to one accountable organisation. A relying party who reaches the device can always find who stands behind it, without asking.

Prerequisites

  • The device's ECZ-ID, or the parent ECZ-ID it names.

Example

bashRuns anywhere. No key, no account.
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json

Expected output

jsonMeasured 2026-10-09 · abridged
{
  "ecz_id": "ECZ-GB-RBS1NW",
  "resolver_v2": {
    "record_type": "ECZ_ID_PARENT",
    "state": {
      "lifecycle_state": "ACTIVE",
      "parent_tier": "VERIFIED",
      "reliance_level": "PARENT_IDENTITY_ONLY"
    },
    "reliance": {
      "can_rely_on": ["record_existence", "active_parent_state",
                      "current_parent_tier", "public_resolver_visibility"],
      "reverify_before_reliance": true
    }
  }
}

This is the operator record behind this site. Read parent_tier_context.tier_meaning on a child record: a parent tier is never inherited by a machine.

What this proves

  • An accountable organisation exists and is publicly resolvable.
  • Its current lifecycle state and parent tier, at the moment you read them.

What this does not prove

  • That the organisation authorised any particular action by the device.
  • That the machine itself is verified, safe or compliant.

Next: Establish authority →

Step 03 of 8

Establish authority#

Authority

Live · signed-in operation in TrustOps

Live in production and operated by the account holder, signed in, through the TrustOps Dashboard.

GoalHave an owner or admin authorise an ECZ-ID Intent for the controller in the TrustOps Dashboard.

Why it mattersAuthority is who authorised the work, recorded where a stranger can later ask. A Passport, a payment or a binding never grants it.

Prerequisites

  • The controller's child Passport.
  • An ECZ-ID Intent plan, purchased in TrustOps.
  • An owner or administrator to authorise. Nobody authorises their own request.

Example

jsonIllustrative. The authorisation block Core records on an Intent; placeholder values.
"authorisation": {
  "kind": "PRINCIPAL_ASSERTION",
  "principal_ref": "user:finance-admin-01",
  "principal_role": "ADMIN",
  "authorised_at": "2026-10-11T09:14:02Z",
  "evidence_digest": "sha256:<64 hex>"
}

What this proves

  • Who authorised the controller's work, in what role, and when, as ECZ-ID Core recorded it.

What this does not prove

  • Any permission in your own access systems. OAuth scopes and API keys stay yours.
  • That runtime actions stay inside the bounds. That needs a decision before the action.
  • Anything public: the public Intent disclosure is not enabled.

Next: Read current state →

Step 04 of 8

Read current state#

Current state

Live · public

Live in production and publicly readable, with no account and no key.

GoalRead the device’s published state live before sending it a command.

Why it mattersA suspended or revoked device should not receive consequential commands, whatever the controller believes.

Prerequisites

  • The device’s ECZ-ID.

Example

jsRuns today. Node 18+.
// Runs today: your own policy over live Resolver state.
async function mayProceed(eczId) {
  const res = await fetch('https://api.ecocitizenz.com/api/p/' + eczId + '.json');
  if (res.status === 404) return { ok: false, why: 'NO_PUBLIC_ECZ_ID_FOUND' };
  if (!res.ok) return { ok: false, why: 'RESOLVER_UNREADABLE', retry: true };
  const v2 = (await res.json()).resolver_v2;
  const failClosed = v2.machine_policy.fail_closed_states;
  const ok = v2.state.lifecycle_state === 'ACTIVE'
    && !failClosed.includes(v2.state.lifecycle_state)
    && !failClosed.includes(v2.binding.state);
  return { ok, why: v2.state.reliance_level };
}

What this proves

  • The device’s published lifecycle and binding state now.

What this does not prove

  • Its physical condition. A record is not a sensor.

ReferenceCurrent state

Next: Propose the command →

Step 05 of 8

Propose the command#

Intent

Live · signed-in operation in TrustOps

Live in production and operated by the account holder, signed in, through the TrustOps Dashboard.

GoalState what the controller is authorised to do, within what bounds and for how long, and make every consequential action name that Intent.

Why it mattersDescribe each command as an action with parameters, so it can be checked against the Intent. Parameter rules bound values; approval rules hold the riskiest commands for a human.

Prerequisites

  • An authorised ECZ-ID Intent in force (state ACTIVE).
  • A single choke point in your code where consequential actions pass.

Example

jsonIllustrative, abridged. Field names from the Core contract ecz.intent.v1; placeholder identifiers.
{
  "schema": "ecz.intent.v1",
  "actor": { "kind": "ECZ_PASSPORT", "ecz_id": "ECZ-GB-EXAMPL::SERVICE_WORKLOAD_PASSPORT-AB12CD", "family": "SERVICE_WORKLOAD" },
  "objective_summary": "Adjust irrigation valves within agreed limits",
  "targets": ["ECZ-GB-EXAMPL::IOT_DEVICE-AB12CD"],
  "constraints": {
    "actions": { "permitted": ["valve:set_position"], "excluded": ["valve:override_*"] },
    "parameters": [{ "path": "position_pct", "op": "lte", "value": 80 }],
    "approval": { "required_for": ["valve:open_full"], "above_amount": null },
    "rate": { "max_actions": 6, "window_seconds": 3600 }
  },
  "window": { "not_before": "2026-10-12T00:00:00Z", "expires_at": "2026-10-19T00:00:00Z" },
  "state": "ACTIVE"
}

In your system

jsIllustrative. Your audit log, written before each consequential action.
// Your audit log: one entry per consequential action, written before it runs.
// ECZ-ID defines the Intent reference; the rest belongs to your system.
log.info('consequential_action', {
  actor_ecz_id: 'ECZ-GB-EXAMPL::SERVICE_WORKLOAD_PASSPORT-AB12CD',
  intent_ref: { intent_id: 'INT-0000000000EXAMPLE0', version: 3 },
  request_id: crypto.randomUUID(),
  action: 'valve:set_position',
  target: 'ECZ-GB-EXAMPL::IOT_DEVICE-AB12CD',
  parameters_digest: 'sha256:<digest of { position_pct: 60 }>',
});

Request identifiers, nonces and correlation IDs belong to the request, not the Intent. ECZ-ID defines no header for carrying an Intent reference over arbitrary protocols, and this site does not invent one.

What this proves

  • What was authorised, as a versioned record with its bounds and window.

What this does not prove

  • That any action stayed inside those bounds. Holding an Intent identifier authorises nothing by itself.
  • That a sandbox Intent covers production. It never does.

Next: Respect the physical consequence boundary →

Step 06 of 8

Respect the physical consequence boundary#

DecisionAction

Release-gated · interface not public

Documented ahead of release.

GoalDecide before a command changes anything physical, because physical changes are the hardest to undo.

Why it mattersA device command can change state, resources, custody or infrastructure in the real world. IntentControl is designed to decide before that boundary; until it is released, your controller’s own policy decides.

Prerequisites

  • A single point in your controller where commands are issued.

Example

jsRuns today: local policy over live Resolver state. Node 18+.
// Runs today: your own policy over live Resolver state.
async function mayProceed(eczId) {
  const res = await fetch('https://api.ecocitizenz.com/api/p/' + eczId + '.json');
  if (res.status === 404) return { ok: false, why: 'NO_PUBLIC_ECZ_ID_FOUND' };
  if (!res.ok) return { ok: false, why: 'RESOLVER_UNREADABLE', retry: true };
  const v2 = (await res.json()).resolver_v2;
  const failClosed = v2.machine_policy.fail_closed_states;
  const ok = v2.state.lifecycle_state === 'ACTIVE'
    && !failClosed.includes(v2.state.lifecycle_state)
    && !failClosed.includes(v2.binding.state);
  return { ok, why: v2.state.reliance_level };
}

What this proves

  • Your controller’s decision, on current published state.

What this does not prove

  • An Intent-bounded decision. IntentControl is release-gated.
  • What physically happens next. ECZ-ID does not observe the physical world.

Next: Report the result, as a report →

Step 07 of 8

Report the result, as a report#

ActionEvidence

Runs in your system

This happens in the integrator’s own system.

GoalRecord what the device reported after the command.

Why it mattersWhat a device reports is the device’s report. It is evidence of what was reported, not proof of what physically happened.

Prerequisites

  • Your controller’s command log.

What this proves

  • What the device reported, as your system recorded it.

What this does not prove

  • The physical outcome. A sensor can be wrong, and a report can be false.

ReferenceEvidence

Next: Receipt and re-verify →

Step 08 of 8

Receipt and re-verify#

ReceiptEvidenceRe-verify

Recorded · no public retrieval

ECZ-ID Core records it, but public, unauthenticated retrieval and verification are not enabled.

GoalKeep a record of what the device did, and know what ECZ-ID publishes about it.

Why it mattersReceipts record what Core recorded about the device’s events. A Receipt proves what the recording system recorded; it does not prove every real-world assertion behind it.

Prerequisites

  • Your own audit log entry from the Intent step.

Example

bashRead the evidence block of a public record.
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json

Expected output

jsonMeasured 2026-10-09 · resolver_v2.evidence, abridged
"evidence": {
  "public_receipts_count": 0,
  "public_receipt_state": "NO_PUBLIC_RECEIPT",
  "public_receipt_retrievable": false,
  "receipt_retrieval_state": "NONE_PUBLISHED",
  "recorded_receipts_count": 1,
  "reason_codes": ["RECEIPTS_RECORDED_NOT_PUBLICLY_RETRIEVABLE"]
}

Recorded is not published. Decide on evidence.public_receipt_state and evidence.public_receipt_retrievable, the fields the record lists for machines.

What this proves

  • Whether Core has recorded receipts for the subject, and whether any are publicly retrievable.

What this does not prove

  • What any receipt says. Public retrieval and public verification are not enabled.
  • That an action happened or succeeded. An allow is not a result, and a decision receipt is not an action receipt.

Where next

This page is explanation, not authority. Current state is only ever the live Resolver record. Commands were run read-only on 2026-10-10; illustrative examples are labelled.