Skip to content
EcoCitizenZ
Quickstart

One integration, end to end.

Read a real public record. Check a target from your own machine. Get a free Passport for something you operate, bind it to an identity you already have, and put a policy on the result.

Every command on this page was run against live production surfaces on 2026-09-22, from a clean directory, with no account and no credential. Where something cannot be run that way, this page says so instead of printing a command that fails.

The sixty-second version

ECZ-ID answers one question in public: who is accountable for this machine, and what have they published about it? Nothing else about your system changes. It is not a safety signal, and no record is not a verdict.

Two commands below need no account, no key and no install. If neither of them is useful to you, stop reading — that is the product, and it is better to find out in a minute than in a sprint.

Both of these run right now
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json

npx -y @ecocitizenz/ecz-id-mcp-verifier check --target ECZ-GB-RBS1NW
01

Know what you are adding

What does ECZ-ID give my system that it does not already have?

One thing: a public, resolvable answer to "who is accountable for this machine, and what is currently published about it?" — an identifier a stranger can look up without asking you, without an account, and without your cooperation at the moment they need it.

That is the whole product. ECZ-ID does not run your agent, proxy your traffic, hold your keys, sign your releases or sit in your request path. Your authentication stays yours. Your authorisation stays yours. Your transport, your framework, your deployment and your data all stay exactly as they are.

And it is not a safety signal. A record never means the machine is safe, secure, correct, tested or approved, and the absence of a record never means the opposite. The record tells you who stands behind a thing and what they have published. What that is worth is your policy decision, every time.

You may rely on

  • A durable identifier for one logical machine, and one accountable operator behind it
  • A public record any relying party can read, live, with no account
  • A machine-readable projection you can put a policy on

You may not infer

  • Safety, security, correctness or approval of the machine
  • Certification, compliance, insurance or platform endorsement
  • Any claim at all when no record exists — absence is absence
02

Read a real record

What does a public ECZ-ID record actually look like?

Start here, before you decide anything. This is a real production record — the operator of this site — and it needs no install, no key and no account. One HTTP GET.

The machine record and the human page live on different hosts. Automate against api.ecocitizenz.com; send people to resolver.ecocitizenz.org. Pointing a script at the human host returns a page, not a record, and that is the commonest first mistake.

Runs anywhere. No account.
curl -s https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json
Abridged from the live response on 2026-09-22
{
  "ecz_id": "ECZ-GB-RBS1NW",
  "entity_name": "EcoCitizenz",
  "country_code": "GB",
  "parent_tier": "Verified",
  "status": "active",
  "children_count": 0,
  "resolver_v2": {
    "schema_version": "2.0",
    "record_type": "ECZ_ID_PARENT",
    "canonical_resolver_url": "https://resolver.ecocitizenz.org/p/ECZ-GB-RBS1NW",
    "canonical_machine_json_url": "https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json",
    "state": {
      "lifecycle_state": "ACTIVE",
      "parent_tier": "VERIFIED",
      "reliance_level": "PARENT_IDENTITY_ONLY"
    },
    "binding": {
      "state": "NO_PUBLIC_PROOF",
      "reason_code": "NO_PUBLIC_BINDING_PROOF_AVAILABLE"
    },
    "reliance": {
      "can_rely_on": [
        "record_existence",
        "active_parent_state",
        "current_parent_tier",
        "public_resolver_visibility"
      ],
      "do_not_infer": ["legal_name", "domain_control", "safety", "compliance", "..."],
      "reverify_before_reliance": true
    },
    "machine_policy": {
      "recommended_machine_action": "ALLOW_PARENT_IDENTITY_VISIBILITY_ONLY",
      "high_risk_action": "FAIL_CLOSED_UNLESS_LIVE_BINDING_AND_EVIDENCE_AVAILABLE",
      "fail_closed_states": ["REVOKED", "SUSPENDED", "DEGRADED", "MISMATCH", "..."]
    }
  }
}

You may rely on

  • resolver_v2.state — the current lifecycle state and parent tier
  • resolver_v2.reliance.can_rely_on — what this record will support
  • resolver_v2.machine_policy — which states the publisher expects you to fail closed on

You may not infer

  • Anything in resolver_v2.reliance.do_not_infer — the record names its own limits, and they are binding
  • A cached copy. There is no cache. Read it live whenever state matters
03

Check a target from your own machine

How do I check something without writing a client?

The verifier is published, free, read-only and needs no account. It classifies a target, reads the public Resolver posture where one applies, returns a deterministic result and routes you onward. It never writes truth, activates proof or marks anything bound — and it never uploads your source, your secrets or any telemetry.

Run it against our record and you get a result you can compare against the JSON you just read by hand.

No install. No account.
npx -y @ecocitizenz/ecz-id-mcp-verifier check --target ECZ-GB-RBS1NW
Verbatim, abridged for length — 2026-09-22
{
  "schema_version": 1,
  "verifier": "ECZ-ID MCP Verifier",
  "verifier_version": "0.9.0",
  "target": "ECZ-GB-RBS1NW",
  "target_type": "ecz_id",
  "policy_mode": "OPEN",
  "operator": "unknown",
  "result_state": "RESOLVER_VERIFIABLE",
  "reason_codes": [],
  "resolver_url": "https://resolver.ecocitizenz.org/p/ECZ-GB-RBS1NW",
  "machine_json_url": "https://api.ecocitizenz.com/api/p/ECZ-GB-RBS1NW.json",
  "primary_action": "VIEW_RESOLVER_PROOF",
  "secondary_actions": ["RECHECK_BEFORE_RELIANCE"],
  "backend_remains_final_authority": true,
  "verifier_writes_truth": false,
  "verifier_activates_proof": false,
  "verifier_marks_bound": false,
  "local_policy_decides": true,
  "recheck_before_reliance": true,
  "no_safety_or_approval_inference": true,
  "no_source_uploaded": true,
  "no_secrets_uploaded": true,
  "no_telemetry": true,
  "exit_code": 0
}

You may rely on

  • result_state — one of a fixed, documented set
  • exit_code — deterministic, so CI can branch on it
  • The routing fields, which tell you where to go next and nothing more

You may not infer

  • NO_PUBLIC_RESOLVER_PROOF_FOUND as a negative verdict. It means no record was found, and nothing else
04

Get a free Passport for a machine you operate

How do I get one of these for my own agent or MCP server?

Free means free: no payment, no card, no trial and no plan. The acquisition contract states payment_required: false and payment_calls: 0, and the free lane never touches commerce. You authenticate once, in your own browser, because a Passport is issued to an operator and an operator has to be someone.

Which families are open changes without a release on our side, so this page does not hold a list — it reads the contract live, above. Where a family is not open yet the honest answer is "not open yet", which is a destination and not an error, and no call to action is shown that cannot complete.

One subject, one ECZ-ID. A second deployment, replica, region, version or platform is not a second machine and must not become a second Passport. Ask for the same subject twice and you are handed back the identity you already had — that is a success, not a collision.

Read the contract from your server or your CLI, not from browser code on another origin. TrustOps returns no Access-Control-Allow-Origin header on any public surface, so a cross-origin browser read is blocked — which is why the panel below usually reports that it could not read it. The public machine record does send that header and is browser-readable.

Read which families are open, live
curl -s https://trustops.ecocitizenz.com/api/v1/acquisition/families

Which families are open, right now

read the contract yourself

Read from ecz.free_passport_acquisition.v1 in your browser as this page loaded, not baked in when the site was built.

Reading the acquisition contract…

You may rely on

  • families[].available — render a call to action only where this is true
  • families[].start_path — where an operator completes the journey, in a browser
  • families[].identifier_kinds — the subject locators that family accepts

You may not infer

  • A hard-coded family list. Availability changes with no release; read it every time
  • Treating NOT_YET_LIVE as an error, a failure or a dead button

What you cannot do yet

The headless six-step journey (session, identity, organisation, machine, issue) is described in the SDK endpoint contract, but those routes answer HTTP 404 on production TrustOps today, so there is no scriptable path and this page does not publish one. The browser journey at the family start path is the supported route. Nothing on this site can issue a Passport, and nothing should try.

05

Bind it to a native identity you already have

How does the Passport attach to my actual system?

By declaring it on a surface you already control, in the format that surface already uses. ECZ-ID does not invent a new place to put identity — it points at the ones your ecosystem already reads.

An MCP server declares a .well-known/ecz-mcp.json at its operator origin and carries its registry name. An agent carries its ECZ-ID and a resolver backlink in its A2A card, or a .well-known/ecz-agent.json. An OpenAPI description carries x-ecz-id. A repository or a published package binds by its own canonical name.

The declaration is a pointer, not proof. It says "look here"; the Resolver says what is currently true. A binding that claims control it cannot demonstrate is exactly what the record refuses to infer for you.

A live declaration, as served today
curl -s https://developers.ecocitizenz.com/.well-known/ecz-mcp.json
06

Relate independent entities

How do separately-owned machines relate to each other?

Through the Digital Entity Graph, which has four lanes and — deliberately — no fifth one for authority.

Your organisation OPERATES a subject. The subject IS one machine, under that family’s subject law: one logical MCP server, one logical agent. The subject IS REPRESENTED BY its bindings — a registry entry, a remote endpoint, a local stdio launch, a desktop bundle. Every one of those is the same subject wearing different clothes, and none of them is ever a second Passport. Then the subject RELATES TO other families: an MCP server relates to API, Service & Workload, SDK and Agent, and an agent relates back to MCP, API, Service & Workload and SDK.

All four lanes come off the live contract — subject_law, binding_classes, adjacent — so a relationship cannot exist in a picture without existing in the record first.

The fifth lane is missing on purpose, and it is the one everybody reads in anyway. A line between two boxes looks like permission. None of these lines is permission. The record establishes which organisation operates an entity, that organisation’s own tier, which representations it has bound to one identity, and what the record said at the moment you read it. It does not establish that a connected entity may act for this one, that any scope or credential has been delegated, or that a relationship shown is currently live in production. Authority is granted in your systems. ECZ-ID does not grant it, infer it or enforce it.

The graph is the shape. The Reciprocal Reliance Envelope is what you write down when you actually resolve both sides at run time — each half carrying the other’s posture, each reading PUBLIC_RECORD_FOUND, NO_PUBLIC_ECZ_ID_FOUND or RESOLVER_UNREADABLE, and the third is not the second. Its external_authorisation field is pinned at not_determined_by_eczid, which is the same sentence in machine form.

A new Passport starts with no bindings and no relationships. An empty graph is the normal state of a new record, not a deficient one.

07

Put a policy on it

What do I actually do with the answer?

Decide. ECZ-ID reports state and refuses to make the decision for you, so the last step is always yours, and it is short.

Three rules survive every integration. Read live, because there is no cache and state moves. Treat unreadable as unreadable — a 429 or a 503 is a retry, never a downgrade of the counterparty. And treat absence as absence: no record is NO_PUBLIC_ECZ_ID_FOUND, which is not unsafe, not untrusted and not a failed check.

The whole integration, in one function
// Read live. Never cache a record; never treat absence as a verdict.
const res = await fetch(`https://api.ecocitizenz.com/api/p/${eczId}.json`);

if (res.status === 404 || res.status === 410) {
  // NO_PUBLIC_ECZ_ID_FOUND. Not unsafe, not untrusted, not a failed check.
  return { record: null, retryable: false };
}
if (res.status === 429 || res.status === 503) {
  // Unreadable is not absent. Retry; do not downgrade the counterparty.
  return { record: null, retryable: true };
}

const record = await res.json();
const v2 = record.resolver_v2;

// Your policy. ECZ-ID reports; it does not decide for you.
const allow =
  v2.state.lifecycle_state === 'ACTIVE' &&
  !v2.machine_policy.fail_closed_states.includes(v2.state.lifecycle_state);

return { record, allow, recheckBeforeReliance: true };
08

Hold the line in CI

How do I stop this regressing?

The same deterministic check runs as a GitHub Action with an explicit policy. REQUIRE exits 1 when no public record is found; OPEN reports and passes. Both exit codes were measured against the live Resolver.

It reads only. It does not mutate your repository, upload your source or your secrets, or emit telemetry.

.github/workflows/ecz-id.yml
# Fails the job when the target has no public record. Exit 1, measured.
- uses: Ecocitizenz/ecz-id-mcp-verifier@v0.9.0
  with:
    target: ${{ env.COUNTERPARTY_ECZ_ID }}
    policy: REQUIRE

Exit codes

The full set the verifier documents. Two of them were produced while writing this page; the rest are the published contract, and are marked as such rather than presented as things we watched happen.

CodeMeaningMeasured here
0Resolver-verifiable, or missing proof under OPEN policyYes, on 2026-09-22
1Policy required proof and none was foundYes, on 2026-09-22
2Deterministic mismatchPublished contract
3Revoked, suspended or expiredPublished contract
4Unsupported target or invalid inputPublished contract
5Network or timeout where policy requires fail-closedPublished contract
6Internal verifier errorPublished contract

The boundary, stated once

ECZ-ID Core

Writes canonical truth and issues identity. Not a surface you call.

TrustOps

Acquisition, setup and lifecycle. Where a free Passport is obtained.

Resolver

The public record. Read it live, every time state matters.

This site

Documents and routes. It never issues, verifies, or holds state.

Re-check the public record before reliance. Local policy decides.