Skip to content

docs(guides): add the demo runbook and its three silent defaults - #383

Open
LKSNDRTMLKV wants to merge 2 commits into
mainfrom
docs/demo-runbook
Open

LKSNDRTMLKV wants to merge 2 commits into
mainfrom
docs/demo-runbook

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

The end-to-end path from the shipped demo corpus to a resolving passport, traced in source rather than assembled from memory.

Docs only. No code changed.

Why this exists

The project has never demonstrated itself end to end, and its most distinctive
capabilities — audience-scoped disclosure, and identity without GS1 — are
shipped and invisible. Tracing the path turned up three defaults that break the
demonstration, two of them silently.

🚨 The two silent ones

RESOLVER_BASE_URL defaults to https://id.odal-node.io, which is NXDOMAIN.
Checked by lookup: the apex and docs. resolve; id. does not.
build_carrier_url writes that base into every passports qr_code_url at
publish, and the passport is then signed — so the dead URL is inside the
signature and cannot be corrected without reissuing. Every demo QR scans to
nothing. Filed separately as #382, since changing a signature-affecting default
is a decision rather than a doc fix.

CREDENTIAL_ISSUERS_SELF unset makes the credentialed route serve the public
body with a 200.
Without it credential_trust is Ghost, credentials_live
is false, and the credential directory is never wired. From main.rs: "with
none, the audience-scoped route serves the public view"
. Nothing errors, both
views render, and the demo shows no contrast at all — which for this
particular demonstration is the worst possible outcome, because the contrast is
the entire argument.

✅ A correction worth recording: it works on a laptop

Credential verification resolves the issuers did:web, and every such fetch
passes url_guard::assert_public_targethttps required, loopback and
private ranges refused
. Read alone, that says a local demo is impossible, and
I nearly documented it that way.

It is not. HttpCredentialDirectory::with_local_issuer resolves a credential
the node issued to itself
in-process, never touching the guard, and
main.rs wires it whenever credentials are live. So the full public /
credentialed contrast runs on localhost with no domain, no TLS and no tunnel.

A credential from another issuer still needs that issuers DID publicly
resolvable — the guard doing its job.

⚠️ One real limit, stated rather than discovered

Self-trust grants legitimate interest only, deliberately: an authoritys
standing is conferred by a member state, not asserted by an operator. Public vs
repairer works out of the box; the authority tier needs a second identity named
in CREDENTIAL_ISSUERS_AUTHORITY.

The commands are the real ones

First draft invented odal passport create and guessed the credential flags.
Both were wrong and are corrected against cli_args.rs:

  • passports are created with odal import (which takes JSON as well as CSV/TSV) and signed with odal publish
  • odal credential issue takes --holder-did, --name, --role, --country, --product-groups, --valid-for-days

Every command in the guide — init, up, down, status, bootstrap,
import, validate, publish, list, credential issue — was checked against
the parser, and --node-url against Inits own flags.

The part worth putting on screen

build_carrier_url branches on whether the data carries a GTIN:

Scheme Carrier Needs GS1 membership?
1 — GS1 {base}/01/{gtin}/21/{serial} Yes
2 — Identification Link {base}/dpp/{id} No
3 — DID {base}/dpp/{id} No

Requiring a GTIN means requiring GS1 membership. Schemes 2 and 3 exist so a
manufacturer without a Company Identification Number can still issue a
conformant passport — and two passports side by side, with two different carrier
forms, make that concrete in a way no sentence does.

Related

@LKSNDRTMLKV LKSNDRTMLKV added urgency/next Blocks work already scheduled surface/docs-only Contained to documentation; no code depends on it labels Sep 19, 2026
@LKSNDRTMLKV

Copy link
Copy Markdown
Member Author

Self-correction: an earlier revision of this branch leaked a private path, and it was pushed.

The "Where this runs" table described a staging rung by naming an internal
evidence directory and reporting its state. That is a path into a
non-public repository, which CLAUDE.md §6a forbids on this surface — it
discloses both that the repository exists and something about its operational
state.

Removed in c522c9f. The table now describes deployment in terms a reader of this
repository can act on, without naming anything they cannot open:

| A hosted environment | Depends on your deployment | A first deployment is a poor thing to attempt during a demo week. Do it early enough to fail once |

The sovereignty paragraph under it was reworded the same way — it now states the
design rule rather than describing our own arrangement.

The leak was live on a pushed branch of a public repository. Force-rewriting
history would not help: the branch was public while it stood, so the correct
response is the fix plus this note, not a quiet amend.

Re-scanned everything else I have pushed to a public repository in this session
— every PR and issue body, every comment and inline review reply, and the full
diff of every branch across dpp-core, dpp-engine and dpp-web — against private
repository names, paths into them, and internal decision-record references. This
was the only hit.

One near-miss worth recording: site/dpp-docs/ in the web repository matches the
same pattern and is not a leak — it is that repositorys own directory for
the documentation site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

surface/docs-only Contained to documentation; no code depends on it urgency/next Blocks work already scheduled

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant