Skip to content

Guided flow · ECZ-ID Plugin Passport™

Plugin guided flow

Carry a published plugin through the Accountability Chain: its publisher, its identity across every marketplace, accountable distribution, the difference between host permissions and authority, Intent where it acts, and evidence.

  1. Publisher
  2. Plugin identity
  3. Accountable distribution
  4. Permissions vs authority
  5. Intent
  6. Operation
  7. Evidence and re-verify

Steps at a glance · what is live today

  1. 01Identify the publisherLive · public
  2. 02Identify the pluginLive · public
  3. 03Distribute accountablyLive · public
  4. 04Keep permissions and authority apartYour system
  5. 05Carry IntentLive · signed in
  6. 06Operate, deciding before consequenceRelease-gated
  7. 07Evidence and re-verifyLive · public

Step 01 of 7

Identify the publisher#

IdentityAuthority

Live · public

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

GoalResolve the Business Passport of the publisher that answers for the plugin.

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

Prerequisites

  • The plugin'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 plugin.
  • That the machine itself is verified, safe or compliant.

Next: Identify the plugin →

Step 02 of 7

Identify the plugin#

Identity

Live · public

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

GoalGive the plugin a free ECZ-ID Passport and declare it where its ecosystem already looks.

Why it mattersOne plugin is one Passport across every marketplace. Versions, releases, installs and executions are bindings, evidence or events, never new identities.

Prerequisites

  • A plugin you publish.
  • One identifier: its plugin name, manifest URL, repository or package.

Example

jsonIllustrative declaration in a plugin manifest. Use the values your TrustOps console shows.
{
  "name": "example-plugin",
  "version": "1.4.0",
  "ecz_id": "ECZ-GB-EXAMPL::PLUGIN_PASSPORT-AB12CD",
  "resolver_backlink": "https://resolver.ecocitizenz.org/p/ECZ-GB-EXAMPL::PLUGIN_PASSPORT-AB12CD"
}

Check

bashCheck it from the relying side. No install, no account.
npx -y @ecocitizenz/ecz-id-mcp-verifier check --target https://github.com/Ecocitizenz/ecz-id-mcp-verifier --target-type github_repo

Expected output

jsonMeasured 2026-10-09 · abridged
{
  "target_type": "github_repo",
  "policy_mode": "OPEN",
  "result_state": "NO_PUBLIC_RESOLVER_PROOF_FOUND",
  "reason_codes": ["NO_PUBLIC_RESOLVER_PROOF_FOUND", "RESOLVER_READ_ONLY",
                   "LOCAL_POLICY_DECIDES"],
  "primary_action": "CHOOSE_OPERATOR_PATH",
  "exit_code": 0
}

The verifier’s own repository holds no Plugin Passport yet, and the verifier says exactly that.

What this proves

  • That the machine has a persistent, publicly resolvable identity under an accountable organisation, once issued and declared.

What this does not prove

  • That the machine is safe, secure, correct or verified. A free Passport never claims that.
  • Control of any native identity. A declaration is a pointer; only a proven binding proves.

Next: Distribute accountably →

Step 03 of 7

Distribute accountably#

Identity

Live · public

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

GoalMake every listing of the plugin point back to the same identity and the same publisher.

Why it mattersThe same plugin appears on several marketplaces and hosts. An installer on any of them should reach one identity, not one per listing.

Prerequisites

  • The plugin’s Passport.
  • Its manifest in each host format you ship.

Example

jsonIllustrative declaration in a plugin manifest. Use the values your TrustOps console shows.
{
  "name": "example-plugin",
  "version": "1.4.0",
  "ecz_id": "ECZ-GB-EXAMPL::PLUGIN_PASSPORT-AB12CD",
  "resolver_backlink": "https://resolver.ecocitizenz.org/p/ECZ-GB-EXAMPL::PLUGIN_PASSPORT-AB12CD"
}

Marketplace listings, GPT actions, store apps and GitHub Action references are binding classes of one Passport. A class being defined is not a claim of a live integration with that marketplace.

What this proves

  • That each listing names the same identity and publisher.

What this does not prove

  • That a marketplace has reviewed or approved the plugin.
  • Control of a listing, until a binding is proven.

Next: Keep permissions and authority apart →

Step 04 of 7

Keep permissions and authority apart#

AuthorityIntent

Runs in your system

This happens in the integrator’s own system.

GoalSeparate what a host lets the plugin touch from what anyone has authorised it to do.

Why it mattersA host grants permissions: scopes, file access, API reach. Those are permissions, granted by the host and the installer. Authority is who authorised a particular piece of work; Intent is its bounds. A plugin with broad permissions can still be acting outside any authorisation.

Prerequisites

  • The permission list your plugin requests in each host.

What this proves

  • Nothing on its own. This is a design discipline that makes the next steps meaningful.

What this does not prove

  • That host permissions are appropriate. That is the host’s and installer’s decision.

Next: Carry Intent →

Step 05 of 7

Carry Intent#

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 plugin is authorised to do, within what bounds and for how long, and make every consequential action name that Intent.

Why it mattersOnly plugins that take consequential actions need an Intent. When they do, it bounds what the plugin may do within the permissions a host already granted.

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::PLUGIN_PASSPORT-AB12CD", "family": "PLUGIN" },
  "objective_summary": "Open pull requests that update dependency versions",
  "constraints": {
    "actions": { "permitted": ["repo:open_pull_request"],
                 "excluded": ["repo:push_default_branch", "repo:delete_*"] },
    "destinations": { "permitted": ["github.com/example-org/*"], "excluded": [] },
    "rate": { "max_actions": 20, "window_seconds": 86400 }
  },
  "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::PLUGIN_PASSPORT-AB12CD',
  intent_ref: { intent_id: 'INT-0000000000EXAMPLE0', version: 3 },
  request_id: crypto.randomUUID(),
  action: 'repo:open_pull_request',
  destination: 'github.com/example-org/payments-api',
});

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: Operate, deciding before consequence →

Step 06 of 7

Operate, deciding before consequence#

Current stateDecision

Release-gated · interface not public

Documented ahead of release.

GoalDecide whether the plugin's proposed action may proceed before it changes anything.

Why it mattersIntentControl is designed to evaluate a proposed action against identity, authority, delegation, Intent, policy, current state and constraints. It is documented ahead of release, so today you decide with your own policy over live state.

Prerequisites

  • A choke point in your code in front of the action.
  • The counterparty or target ECZ-ID.

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 own decision, made on the current published state of the counterparty.

What this does not prove

  • An Intent-bounded decision. That is IntentControl: its interface is release-gated and not callable, and commercially it is coming soon in TrustOps and not yet on sale.
  • Enforcement. A decision only constrains actions routed through an enforcement point you run.

Next: Evidence and re-verify →

Step 07 of 7

Evidence and re-verify#

EvidenceRe-verify

Live · public

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

GoalLet anyone who was not there re-check the plugin's public state, now and in CI.

Why it mattersEvery other link ends with a reader who was not present. Re-verifying closes the chain: the record is re-resolved live, never trusted from a cache.

Prerequisites

  • Node 22.14+ for the verifier (its declared engine), or any HTTP client for the record.

Example

bashREQUIRE fails closed when proof is missing.
npx -y @ecocitizenz/ecz-id-mcp-verifier check --target https://github.com/Ecocitizenz/ecz-id-mcp-verifier --target-type github_repo --policy REQUIRE

# .github/workflows/ecz-id.yml — fails the job when proof is missing
- uses: Ecocitizenz/ecz-id-mcp-verifier@v0.9.0
  with:
    target: ${{ env.COUNTERPARTY_ECZ_ID }}
    policy: REQUIRE

Expected output

jsonMeasured 2026-10-09 · under OPEN
{
  "target_type": "github_repo",
  "policy_mode": "OPEN",
  "result_state": "NO_PUBLIC_RESOLVER_PROOF_FOUND",
  "reason_codes": ["NO_PUBLIC_RESOLVER_PROOF_FOUND", "RESOLVER_READ_ONLY",
                   "LOCAL_POLICY_DECIDES"],
  "primary_action": "CHOOSE_OPERATOR_PATH",
  "exit_code": 0
}

Under REQUIRE the same check exits 1, so a CI job that installs or ships the plugin stops until its proof is published.

What this proves

  • The current published state, at the moment of the check, read live.

What this does not prove

  • Safety, security, certification, compliance or approval.
  • Anything about the state a minute later. Re-check before each reliance.
  • Any ECZ-ID evidence object. The evidence here is your own record of the check; Receipts and LedgerCore anchors are not publicly retrievable.

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.