-
Notifications
You must be signed in to change notification settings - Fork 533
docs: add migration guide for Indy to did:webvh AnonCreds issuance #4059
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
db16ede
fdde90a
e00fe1b
e341f00
96043db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,353 @@ | ||
| # Transitioning AnonCreds Issuance from Indy to did:webvh | ||
|
|
||
| This guide describes how to transition [AnonCreds] **issuance** from a | ||
| [Hyperledger Indy] ledger to the [did:webvh] method. It focuses on the issuance | ||
| process as a whole: issuer setup, verifier readiness, and the cutover from one | ||
| Verifiable Data Registry (VDR) to another. | ||
|
|
||
| **Assumption:** All parties in your ecosystem already support did:webvh | ||
| technologically. Issuers, holders, and verifiers can resolve did:webvh | ||
| identifiers and process AnonCreds rooted in did:webvh. This guide is for when | ||
| that readiness is in place and you are moving issuance from Indy to did:webvh. | ||
|
|
||
| The recommended approach is a **planned cutover**: you stop issuing from Indy | ||
| and start issuing from did:webvh on a chosen date. Existing holders keep their | ||
| Indy-rooted credentials; new issuance uses did:webvh. During and after the | ||
| cutover, the issuer must continue to **manage already-issued Indy credentials** | ||
| (e.g. revocation and other status updates) until those credentials are no longer | ||
| in use. | ||
|
|
||
| [AnonCreds]: https://www.hyperledger.org/projects/anoncreds | ||
| [Hyperledger Indy]: https://www.hyperledger.org/projects/hyperledger-indy | ||
| [did:webvh]: https://identity.foundation/didwebvh/ | ||
|
|
||
| ## Why Migrate? | ||
|
|
||
| Indy-based AnonCreds requires a Hyperledger Indy blockchain network to store | ||
| schemas, credential definitions, and revocation registries. While this model is | ||
| proven and reliable, it ties issuers to a specific ledger network and its | ||
| governance. | ||
|
|
||
| did:webvh offers an alternative Verifiable Data Registry (VDR) for AnonCreds | ||
| that is: | ||
|
|
||
| - **Ledger-independent** -- AnonCreds objects are published as web-hosted | ||
| resources, removing the dependency on a blockchain network. | ||
| - **Web-native** -- Objects are hosted on standard web infrastructure and | ||
| resolved via HTTPS. | ||
| - **Trustworthy** -- A witness model provides cryptographic attestation of | ||
| published objects, replacing the endorser/steward trust model of Indy. | ||
| - **Portable** -- DIDs can optionally be configured for portability across | ||
| servers. | ||
| - **Interoperable** -- Credentials issued with did:webvh use the same AnonCreds | ||
| cryptography; verifiers process them identically to Indy-based credentials. | ||
|
|
||
| For technical background on how ACA-Py supports multiple AnonCreds methods, see | ||
| [Publishing AnonCreds Objects To Other Ledgers/VDRs](../features/AnonCredsMethods.md). | ||
|
|
||
| ## Key Differences at a Glance | ||
|
|
||
| | Aspect | Indy | did:webvh | | ||
| |---|---|---| | ||
| | **Identifier format** | `WgWx...:2:name:1.0` (legacy) or `did:indy:sovrin:WgWx...` | `did:webvh:{SCID}:domain:ns:id/resources/{digest}` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might want to expand the Indy examples to include a the elements of a identifier data type. |
||
| | **Where objects live** | Indy ledger transactions | WebVH server-hosted resources with Data Integrity proofs | | ||
| | **Trust model** | Endorser/Steward signs ledger transactions | Witness attests resource publications | | ||
| | **Revocation tails files** | Uploaded to a tails server | Uploaded to a tails server (same approach) | | ||
| | **ACA-Py API endpoints** | `/anoncreds/*` | `/anoncreds/*` (same endpoints) | | ||
| | **Plugin required** | Built-in (`DIDIndyRegistry`, `LegacyIndyRegistry`) | External (`--plugin webvh`) | | ||
|
|
||
| The important thing to note is that the ACA-Py admin API endpoints remain the | ||
| same. The only difference from a controller's perspective is the `issuerId` | ||
| used when creating objects and the format of the resulting identifiers. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Before starting the migration, ensure the following are in place: | ||
|
|
||
| 1. **`askar-anoncreds` wallet type** -- Your ACA-Py instance must be running | ||
| with `--wallet-type askar-anoncreds`. If you are still on the legacy `askar` | ||
| wallet type, complete the | ||
| [AnonCreds Controller Migration](AnoncredsControllerMigration.md) first. See | ||
| also [The askar-anoncreds Wallet Type](AnonCredsWalletType.md) for | ||
| background. | ||
|
|
||
| 2. **WebVH plugin installed** -- The did:webvh AnonCreds registry is provided | ||
| by the `webvh` plugin in [acapy-plugins]. Install it and load it with | ||
| `--plugin webvh`. See the | ||
| [WebVH plugin README](https://github.com/openwallet-foundation/acapy-plugins/tree/main/webvh) | ||
| for installation instructions. | ||
|
|
||
| 3. **WebVH server running** -- You need a running instance of | ||
| [didwebvh-server-py](https://github.com/decentralized-identity/didwebvh-server-py) | ||
| to host your DID documents and attested resources. | ||
|
|
||
| 4. **Witness configured** -- A witness agent (or self-witnessing configuration) | ||
| must be set up to attest DID registrations and resource uploads. See the | ||
| [WebVH plugin Configuration](https://github.com/openwallet-foundation/acapy-plugins/tree/main/webvh#configuration) | ||
| section for details. | ||
|
|
||
| [acapy-plugins]: https://github.com/openwallet-foundation/acapy-plugins | ||
|
|
||
| ## What Can and Cannot Be Migrated | ||
|
|
||
| **Cannot be migrated:** | ||
|
|
||
| - Existing issued credentials remain Indy-based. Holders keep them as-is | ||
| and verifiers can continue to verify them. There is no mechanism to | ||
| "re-root" an already-issued AnonCreds credential to a different VDR. | ||
|
|
||
| **Must be created under the new DID (new objects, same logical content):** | ||
|
|
||
| - Schemas (same attributes, new `issuerId`) | ||
| - Credential definitions (new cred def linked to the new schema) | ||
| - Revocation registries (automatically created by ACA-Py when a revocable | ||
| cred def is registered) | ||
|
|
||
| **No data loss:** | ||
|
|
||
| - Your wallet retains all Indy objects. ACA-Py supports multiple AnonCreds | ||
| registries simultaneously -- the correct registry is selected automatically | ||
| based on the identifier pattern of the object being accessed. | ||
|
|
||
| ## Migration Strategy: Planned Cutover | ||
|
|
||
| The recommended approach is to **switch issuance** from Indy to did:webvh in one | ||
| planned cutover. During and after the transition, some holders have Indy-rooted | ||
| credentials (issued before the switch) and some have did:webvh-rooted | ||
| credentials (issued after). You do not issue the same credential from both | ||
| VDRs at the same time. The issuer must continue to **manage already-issued Indy | ||
| credentials** — including revocation and other status updates — until they are | ||
| fully phased out. | ||
|
|
||
| ```mermaid | ||
| flowchart LR | ||
| subgraph phase1 ["Phase 1: Prepare"] | ||
| Setup["Set up did:webvh"] | ||
| Share["Share new identifiers with verifiers"] | ||
| end | ||
| subgraph phase2 ["Phase 2: Verifier readiness"] | ||
| Wait["Verifiers update presentation requests"] | ||
| end | ||
| subgraph phase3 ["Phase 3: Switch to did:webvh Issuing"] | ||
| Cutover["Stop Indy issuance, issue only did:webvh"] | ||
| end | ||
| phase1 --> phase2 --> phase3 | ||
| ``` | ||
|
|
||
| ### Phase 1 -- Prepare | ||
|
|
||
| You are currently issuing Indy-based AnonCreds credentials. Prepare the | ||
| did:webvh side without changing issuance yet: | ||
|
|
||
| 1. Set up the did:webvh infrastructure (server, witness, plugin) alongside | ||
| your existing Indy configuration. | ||
| 2. Create a did:webvh DID for your issuer. | ||
| 3. Register your schemas and credential definitions under the new DID (these | ||
| are new objects with the same logical content; only the `issuerId` and | ||
| resulting identifiers change). | ||
| 4. **Share the new did:webvh identifiers with verifiers** (credential | ||
| definition IDs, schema IDs, etc.) so they can add them to their | ||
| presentation requests. During this period, the did:webvh objects exist but | ||
| are **not** used for general issuance. Limited testing (e.g. issuing a | ||
| credential or two to verify the pipeline) is acceptable. | ||
|
|
||
| ### Phase 2 -- Verifier readiness | ||
|
|
||
| Give verifiers time to update their systems to accept credentials rooted in | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expand this a bit more, to indicate that requires two things. Updated libraries that support resolving via the did:webvh AnonCreds Method, and updating the presentation request to add the equivalent did:webvh identifiers as an |
||
| either the existing Indy identifiers or the new did:webvh identifiers. Once | ||
| verifiers can resolve did:webvh and include the new credential definition IDs | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May not be a credential definition -- a presentation request can restrict on DID (issuer), schema and/or credential definition. Might be worth putting in an example here -- before just indy and after Indy or did:webvh |
||
| in their proof requests, you can schedule the cutover. | ||
|
|
||
| ### Phase 3 -- Switch to did:webvh Issuing | ||
|
|
||
| 1. On the chosen date, **stop issuing new Indy credentials**. All new | ||
| issuance uses did:webvh credential definitions only. | ||
| 2. Previously issued Indy credentials remain valid and verifiable for their | ||
| lifetime. They do not need to be re-issued. The issuer must **continue to | ||
| manage** those Indy credentials (e.g. process revocation and other status | ||
| updates) for as long as they are in use. | ||
| 3. You can remove the Indy ledger connection only when all Indy credentials | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps make this Phase 4? Issuer notifies all parties (especially verifiers) that the Indy credentials have all been revoked. Issuers and verifiers (at their leisure) can then remove both the code and the presentation request configuration that uses/references Indy rooted-credentials. Verifiers only do that after all of the credential types they accept have been converted. |
||
| have expired or been revoked and verifiers no longer need to resolve them. | ||
|
|
||
| ## Setting Up did:webvh Issuance | ||
|
|
||
| The steps below provide a high-level overview. For detailed configuration | ||
| and API payloads, refer to the | ||
| [WebVH plugin README](https://github.com/openwallet-foundation/acapy-plugins/tree/main/webvh). | ||
|
|
||
| ### 1. Install and Configure the Plugin | ||
|
|
||
| Add the `webvh` plugin to your ACA-Py startup: | ||
|
|
||
| ```bash | ||
| aca-py start \ | ||
| --wallet-type askar-anoncreds \ | ||
| --plugin webvh \ | ||
| ... | ||
| ``` | ||
|
|
||
| ### 2. Configure Witness | ||
|
|
||
| Configure your agent as a witness (self-witnessing) or connect to an external | ||
| witness agent: | ||
|
|
||
| ``` | ||
| POST /did/webvh/configuration | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "server_url": "https://your-webvh-server.example.com", | ||
| "witness": true | ||
| } | ||
| ``` | ||
|
|
||
| For a controller that relies on an external witness, provide a | ||
| `witness_invitation` instead. See the plugin README for the full witness setup | ||
| flow. | ||
|
|
||
| ### 3. Create a did:webvh DID | ||
|
|
||
| ``` | ||
| POST /did/webvh/controller/create | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "options": { | ||
| "namespace": "my-org", | ||
| "identifier": "issuer-01" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This creates a DID like `did:webvh:{SCID}:your-server.example.com:my-org:issuer-01`. | ||
|
|
||
| ### 4. Register Schemas | ||
|
|
||
| Use the same `/anoncreds/schema` endpoint, but with the new did:webvh DID as | ||
| the `issuerId`: | ||
|
|
||
| ``` | ||
| POST /anoncreds/schema | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "schema": { | ||
| "attrNames": ["name", "date", "degree"], | ||
| "issuerId": "did:webvh:{SCID}:your-server.example.com:my-org:issuer-01", | ||
| "name": "Example Credential", | ||
| "version": "1.0" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The resulting schema ID will be in the format | ||
| `did:webvh:{SCID}:your-server.example.com:my-org:issuer-01/resources/{content_digest}`. | ||
|
|
||
| ### 5. Create Credential Definitions | ||
|
|
||
| ``` | ||
| POST /anoncreds/credential-definition | ||
| ``` | ||
|
|
||
| ```json | ||
| { | ||
| "credential_definition": { | ||
| "issuerId": "did:webvh:{SCID}:your-server.example.com:my-org:issuer-01", | ||
| "schemaId": "<schema_id from step 4>", | ||
| "tag": "default" | ||
| }, | ||
| "options": { | ||
| "support_revocation": true, | ||
| "revocation_registry_size": 1000 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### 6. Revocation Registries | ||
|
|
||
| If the credential definition supports revocation, ACA-Py automatically creates | ||
| and publishes two revocation registries, makes one active, and handles rotation | ||
| -- exactly the same as with Indy. No additional steps are needed. | ||
|
|
||
| ### 7. Issue Credentials | ||
|
|
||
| Use the standard V2.0 issue-credential endpoints. The only difference is that | ||
| the `cred_def_id` in your offer references the did:webvh credential definition | ||
| created in step 5. | ||
|
|
||
| ## Impact on Controllers | ||
|
|
||
| - **Same API, different identifiers** -- The `/anoncreds/*` endpoints are | ||
| identical for Indy and did:webvh. The only change is the `issuerId` you | ||
| supply and the format of the returned object IDs. | ||
| - **After cutover** -- Once you switch to did:webvh issuance, new credential | ||
| offers use did:webvh credential definition IDs. Existing Indy credentials in | ||
| holders' wallets continue to work; no re-issue is required. The controller | ||
| must continue to manage already-issued Indy credentials (e.g. revocation and | ||
| status updates) for as long as they remain in use. | ||
| - **Webhook payloads** -- Webhook events for credential exchange will contain | ||
| the did:webvh identifier formats. Ensure your controller can handle both | ||
| formats during and after the transition (e.g. for any Indy credentials still | ||
| in use). | ||
|
|
||
| ## Impact on Verifiers | ||
|
|
||
| - **Readiness assumed** -- This guide assumes verifiers already support | ||
| did:webvh (resolution and AnonCreds processing). Before the issuer cuts over, | ||
| verifiers should add the new did:webvh credential definition and schema IDs | ||
| to their presentation requests so they accept credentials rooted in either | ||
| VDR during the transition. | ||
| - **Accept both formats during transition** -- After cutover, some holders will | ||
| present Indy-rooted credentials (issued before) and some did:webvh-rooted | ||
| (issued after). Proof requests should reference both Indy and did:webvh | ||
| credential definition IDs, or use attribute-based restrictions that are | ||
| format-agnostic. | ||
| - **No protocol changes** -- The presentation exchange protocol (DIDComm v2, | ||
| present-proof v2) is identical regardless of the underlying VDR. AnonCreds | ||
| is AnonCreds -- the cryptographic operations and proof format are the same. | ||
|
|
||
| ## Frequently Asked Questions | ||
|
|
||
| **Can I use the same schema attributes?** | ||
|
|
||
| Yes. Register a new schema with identical `attrNames` under your did:webvh DID. | ||
| The schema content is the same; only the `issuerId` and resulting identifier | ||
| change. These are new objects, not "re-registered" ones. | ||
|
|
||
| **Do holders need to do anything?** | ||
|
|
||
| This guide assumes holders already support did:webvh (storage and presentation of | ||
| did:webvh-rooted AnonCreds). Existing Indy credentials in holders' wallets | ||
| continue to work unchanged. New credentials issued from did:webvh credential | ||
| definitions are received and stored normally. | ||
|
|
||
| **Why a planned cutover instead of issuing from both VDRs at once?** | ||
|
|
||
| Once the ecosystem supports did:webvh, the recommended path is to switch | ||
| issuance in one cutover. Issuing the same credential from both Indy and | ||
| did:webvh at the same time is not recommended for a given credential | ||
| definition; it complicates lifecycle and verifier logic. After the switch, | ||
| support Indy only for revocation of existing credentials. | ||
|
|
||
| **What happens to my existing Indy credentials after the switch?** | ||
|
|
||
| They remain valid and verifiable for as long as the Indy ledger they were | ||
| published on is operational. Revoking an Indy credential still works through | ||
| the Indy revocation registry. The transition only affects *new* issuance. | ||
|
|
||
| **Do I need to keep my Indy ledger connection after Phase 3?** | ||
|
|
||
| If you have outstanding Indy credentials that may need to be revoked, or if | ||
| verifiers may still need to resolve your Indy-based credential definitions, | ||
| yes. You can remove the Indy ledger connection only when all Indy credentials | ||
| have expired or been revoked and verifiers no longer need to resolve them. | ||
|
|
||
| **Does this model apply to other VDR or format transitions?** | ||
|
|
||
| The pattern described here — prepare new identifiers, share with verifiers, | ||
| then switch issuance and phase out the old VDR — can be adapted when | ||
| transitioning between other credential formats or Verifiable Data Registries. | ||
| This document applies it specifically to Indy → did:webvh. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,6 +147,7 @@ nav: | |
| - Databases: deploying/Databases.md | ||
| - Persistent Queues and Caching: deploying/RedisPlugins.md | ||
| - The askar-anoncreds Wallet Type: deploying/AnonCredsWalletType.md | ||
| - Migrating Issuance from Indy to did:webvh: deploying/IndyToWebVHMigration.md | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change to "Migrating AnonCreds Issuance..." |
||
| - Testing/Troubleshooting: | ||
| - Running and Creating Unit Tests: testing/UnitTests.md | ||
| - Integration Tests: testing/IntegrationTests.md | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A relative minor issue, but let me know what you think -- maybe a note early in the document? What you have in this table the "did:webvh Server AnonCreds Method" where the implementation is opinionated -- there is two extra elements in the DID (
nsandid), plus a defined path to the resource.did:webvhitself can support any scheme -- "plain" did:webvh DIDs (no extra elements), any DID URL path. So we at least need to call that out vs. implying that this is what did:webvh requires.It's too late for this, but I've wondered silently if the path should contain metadata such
/resources/anoncreds-schema/{digest}. Did you consider that? Or the same mechanism that Indy used -- with the object type enumerated in the identifier.