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.
- Device identity
- Controller
- Authority
- Current state
- Proposed command
- Consequence boundary
- Result
- Receipt and re-verify
Steps at a glance · what is live today
- 01Identify the deviceLive · public
- 02Connect the controllerLive · public
- 03Establish authorityLive · signed in
- 04Read current stateLive · public
- 05Propose the commandLive · signed in
- 06Respect the physical consequence boundaryRelease-gated
- 07Report the result, as a reportYour system
- 08Receipt and re-verifyNo public retrieval
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
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-AB12CDThe 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.
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
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.jsonExpected output
{
"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.
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
// 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
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
{
"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
// 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.
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
// 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.
ReferenceConsequence boundaryIntentControl
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
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
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.jsonExpected output
"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.
ReferenceReceiptsEvidenceReceipt reference
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.
