Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/_data/flowIcons.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,59 @@ export default {
<text x="55" y="52" text-anchor="middle" font-size="8" fill="#2f6b4f" font-family="Georgia,serif">&#x2713; verified</text>
</svg>`,

governmentIssue: `<svg viewBox="10 20 60 40" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<!-- Government ID card -->
<rect x="14" y="22" width="52" height="36" rx="3" fill="#f4f0e6" stroke="#1b1a17" stroke-width="1.5"/>
<!-- portrait area -->
<rect x="20" y="28" width="16" height="24" rx="1" fill="#ece6d6" stroke="#c9c0ab" stroke-width="1"/>
<circle cx="28" cy="36" r="4" fill="#c9c0ab"/>
<path d="M21,52 a7,7 0 0,1 14,0 z" fill="#c9c0ab"/>
<!-- state seal in portrait top-right corner -->
<circle cx="34" cy="30" r="4" fill="#1b1a17" opacity=".85"/>
<text x="34" y="33" text-anchor="middle" font-size="5" fill="#f4f0e6" font-family="Georgia,serif">&#x2605;</text>
<!-- text lines right -->
<line x1="42" y1="30" x2="60" y2="30" stroke="#1b1a17" stroke-width="2"/>
<line x1="42" y1="37" x2="58" y2="37" stroke="#c9c0ab" stroke-width="1.5"/>
<line x1="42" y1="43" x2="60" y2="43" stroke="#c9c0ab" stroke-width="1.5"/>
<line x1="42" y1="49" x2="56" y2="49" stroke="#c9c0ab" stroke-width="1.5"/>
</svg>`,

governmentHold: `<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<rect x="26" y="10" width="28" height="50" rx="5" fill="#1b1a17"/>
<rect x="30" y="16" width="20" height="36" rx="2" fill="#f4f0e6"/>
<!-- ID card on screen -->
<rect x="32" y="18" width="16" height="12" rx="1" fill="#ece6d6" stroke="#1b1a17" stroke-width=".75"/>
<!-- portrait thumbnail -->
<rect x="33" y="19" width="5" height="7" rx="1" fill="#c9c0ab"/>
<!-- text lines -->
<line x1="40" y1="21" x2="47" y2="21" stroke="#c9c0ab" stroke-width="1"/>
<line x1="40" y1="24" x2="46" y2="24" stroke="#c9c0ab" stroke-width="1"/>
<line x1="40" y1="27" x2="47" y2="27" stroke="#c9c0ab" stroke-width="1"/>
<!-- seal dot top-right of card -->
<circle cx="46" cy="20" r="2" fill="#1b1a17" opacity=".7"/>
<!-- shield icon on screen (below ID card) -->
<path d="M38,34 l4,-2 l4,2 v5 a4,4 0 0,1 -8,0 z" fill="none" stroke="#1b1a17" stroke-width="1.5"/>
<circle cx="42" cy="39" r="1.25" fill="#1b1a17"/>
<rect x="34" y="56" width="12" height="2" rx="1" fill="#c9c0ab"/>
</svg>`,

governmentDisclose: `<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<!-- full ID (left, crossed out) -->
<rect x="8" y="22" width="34" height="44" rx="2" fill="#ece6d6" stroke="#c9c0ab" stroke-width="1" opacity=".7"/>
<line x1="14" y1="30" x2="36" y2="30" stroke="#c9c0ab" stroke-width="1"/>
<line x1="14" y1="37" x2="36" y2="37" stroke="#c9c0ab" stroke-width="1"/>
<line x1="14" y1="44" x2="36" y2="44" stroke="#c9c0ab" stroke-width="1"/>
<line x1="14" y1="51" x2="32" y2="51" stroke="#c9c0ab" stroke-width="1"/>
<!-- X over full ID lines -->
<line x1="14" y1="30" x2="26" y2="44" stroke="#c0341d" stroke-width="1" opacity=".4"/>
<line x1="26" y1="30" x2="14" y2="44" stroke="#c0341d" stroke-width="1" opacity=".4"/>
<text x="22" y="62" text-anchor="middle" font-size="7" fill="#c9c0ab" font-family="Georgia,serif" font-style="italic">full ID</text>
<!-- derived fact badge (right) -->
<rect x="36" y="30" width="36" height="26" rx="2" fill="#f4f0e6" stroke="#2f6b4f" stroke-width="2"/>
<text x="54" y="43" text-anchor="middle" font-size="8" fill="#2f6b4f" font-family="Georgia,serif" font-weight="bold">AGE 21+</text>
<text x="54" y="52" text-anchor="middle" font-size="8" fill="#2f6b4f" font-family="Georgia,serif">&#x2713; verified</text>
</svg>`,

// Small heading glyphs for the "What X experiences" columns.
// Individual holder (resident / graduate / buyer).
person: `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
Expand Down
39 changes: 39 additions & 0 deletions src/verticals/government/build/index.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: "What are you building? — government IDs &amp; licensing"
description: "Pick your role: wallet (the app the resident uses), issuer (the agency or licensing board), or verifier (a business or service that checks the credential). You only implement your piece."
permalink: /government/build/
---
{# Layer 3 — role picker = conformance-class scoping. #}
<article class="prose">
<h1>What are you building?</h1>
<p>Each role sets your scope <em>before</em> you see any code.</p>

<ul class="role-grid">
<li>
<a href="/government/build/wallet/">
<h3>Wallet</h3>
<p>The app the resident uses to hold their license or certificate.</p>
<p><strong>You implement:</strong> an exchange client — POST, handle VPRs, return VPs.</p>
<p class="effort">~3 things, a few days</p>
</a>
</li>
<li>
<a href="/government/build/issuer/">
<h3>Issuer (agency or licensing board)</h3>
<p>The DMV, state agency, or licensing authority issuing the credential.</p>
<p><strong>You implement:</strong> start an exchange, deliver a signed VC.</p>
<p class="effort">small, separate piece</p>
</a>
</li>
<li>
<a href="/government/build/verifier/">
<h3>Verifier (business or service)</h3>
<p>Whoever checks the credential — a bar, an employer, a government portal.</p>
<p><strong>You implement:</strong> request a presentation, verify the proof.</p>
<p class="effort">small, separate piece</p>
</a>
</li>
</ul>

<p class="note"><strong>You do NOT need to implement the other two.</strong></p>
</article>
167 changes: 167 additions & 0 deletions src/verticals/government/build/issuer.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: "Build an issuer (agency) — government IDs &amp; licensing"
description: "Issue a government credential as a VCALM coordinator: create an exchange, share an interaction URL, poll for the result. A workflow service handles the on-the-wire protocol."
permalink: /government/build/issuer/
---
{# Layer 4 — issuer coordinator. A coordinator is a CLIENT of a workflow
service; it does not run @bedrock/vc-delivery itself. It only creates
exchanges, hosts/shares interaction URLs, and polls for results. #}
<article class="prose">
<h1>Build an issuer</h1>
<p>
You run the agency side — a DMV, a licensing board, or a state agency. As a
<strong>coordinator</strong> you speak VCALM — just the coordinator side of
it. You don't run <em>VCALM delivery</em>, the on-the-wire exchange with the
resident's wallet; a <em>workflow service</em> does that. Your whole job is
three calls: create an exchange, share an interaction URL that points the
resident's wallet at it, then poll the exchange until the wallet has been
issued the credential.
</p>

<aside class="recommended">
<p class="kicker">Recommended</p>
<h2>Drive a workflow service — don't build the protocol</h2>
<p>
The protocol complexity — VCALM delivery, OID4VCI, OID4VP, and
interoperating with whatever wallet a resident brings — is handled by a
<em>workflow service</em>. Digital Bazaar's
<a href="https://github.com/digitalbazaar/bedrock-vc-delivery">@bedrock/vc-delivery</a>
is one. <strong>The workflow service runs that library; you don't.</strong>
</p>
<p>
A goal of VCALM is to let you use a workflow service off the shelf. An
administrator loads the <em>workflows</em> you need (a reusable issuance
config — what to issue, what to ask for). From then on, whenever a resident
action requires a credential exchange, you just create an exchange against
that workflow and read back the result.
</p>
</aside>

<h2>What the coordinator does</h2>
<ol>
<li><strong>Create an exchange</strong> by POSTing the workflow's variables
to its <code>exchanges</code> endpoint.</li>
<li><strong>Host and share an interaction URL</strong> that points the
resident's wallet at that exchange.</li>
<li><strong>Poll the exchange</strong> until it completes, then read the
result.</li>
</ol>
<p>
The workflow service does everything in between — protocol negotiation,
DID authentication, signing the credential, talking to whatever wallet shows
up — and hands you back just the result.
</p>

<h2>1. Create the exchange</h2>
<p>
POST to your workflow's <code>exchanges</code> endpoint. The body carries a
<code>ttl</code> and the <code>variables</code> the workflow expects — here,
who the license is for and its details:
</p>
{% highlight "bash" %}
POST https://workflows.example/workflows/ambulance-license-issuance/exchanges
{% endhighlight %}
{% highlight "json" %}
{
"ttl": 900,
"variables": {
"subject": "did:example:resident456",
"licenseClass": "Ambulance Driver",
"licenseNumber": "ADL-2026-00123",
"validFrom": "2026-01-15T00:00:00Z",
"validUntil": "2028-01-14T23:59:59Z"
}
}
{% endhighlight %}
<p>
This call is authenticated (your service uses a capability or OAuth2 token).
On success the service returns <code>204 No Content</code> with a
<code>Location</code> header — that's your exchange URL:
</p>
{% highlight "http" %}
HTTP/1.1 204 No Content
Location: https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ...
{% endhighlight %}

<h2>2. Host and share the interaction URL</h2>
<p>
The exchange URL <em>is</em> the <code>vcapi</code> interaction endpoint.
Hosting and sharing it is the coordinator's responsibility — put it in a QR
code on a portal confirmation page, typically wrapped in a presentation
request the wallet understands:
</p>
{% highlight "json" %}
{
"VerifiablePresentation": {
"interact": {
"service": [{
"type": "VerifiableCredentialApiExchangeService",
"serviceEndpoint": "https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ..."
}]
}
}
}
{% endhighlight %}
<p>
The resident's wallet POSTs to that endpoint and runs the exchange with the
workflow service directly. DID authentication and credential delivery happen
there — you are not in that loop.
</p>

<h2>3. Poll for the result</h2>
<p>
While the wallet works, GET the exchange URL (authenticated) until its
<code>state</code> is <code>complete</code>:
</p>
{% highlight "bash" %}
GET https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ...
{% endhighlight %}
{% highlight "json" %}
{
"exchange": {
"id": "z19xQ...",
"state": "complete",
"variables": {
"results": {
"didAuthn": {
"did": "did:example:resident456",
"verifiablePresentation": { "...": "the wallet's authenticated VP" }
}
}
}
}
}
{% endhighlight %}
<p>
Verified per-step results land under
<code>exchange.variables.results</code>, keyed by the workflow's step names.
A <code>complete</code> state means the credential was issued into the
resident's wallet.
</p>

<h2>That's it</h2>
<p>
A government issuer, as a coordinator, is these three calls: <strong>create
an exchange, host and share the interaction URL, poll for the result.</strong>
Signing the credential and speaking every wallet's protocol is the workflow
service's job — you just create exchanges and read what comes back.
</p>

<p class="future">
On the wallet end, a standalone client-side VCALM library is still planned;
until it ships, wallets run the exchange over the raw HTTP flow. The
coordinator side shown here works today against any VCALM workflow service,
such as one running
<a href="https://github.com/digitalbazaar/bedrock-vc-delivery">@bedrock/vc-delivery</a>.
</p>

<h2>Go deeper</h2>
<ul>
<li><a href="https://github.com/digitalbazaar/bedrock-vc-delivery">@bedrock/vc-delivery — workflow service</a></li>
<li><a href="https://www.w3.org/TR/vcalm/#did-authentication">VCALM: DID Authentication</a></li>
<li><a href="https://www.w3.org/TR/vc-data-model-2.0/">W3C Verifiable Credentials Data Model 2.0</a></li>
<li><a href="https://www.w3.org/TR/vcalm/">VCALM specification</a></li>
</ul>

<p class="cta"><a class="btn" href="/government/build/">← Back to roles</a></p>
</article>
Loading