Skip to main content

Hyperledger Indy Setup

This guide covers the setup of Hyperledger Indy ledger for identity proofs and credential verification in CCDigital.
Indy/ACA-Py is used for credential proofs during login and user access state synchronization. It provides verifiable credential flows for secure authentication.

Prerequisites

Before starting, ensure you have:
  • Linux or WSL2 with Docker and Docker Compose
  • Python 3.10+ with venv for credential issuance scripts
  • curl and jq for API testing
  • Available ports for ledger and agents (check your architecture)

Component Download

1

Get Indy Ledger Implementation

Obtain an Indy ledger implementation for local development. Options include:
  • von-network (recommended for development)
  • Indy-node standalone setup
git clone https://github.com/bcgov/von-network.git
cd von-network
2

Download ACA-Py Images

Pull ACA-Py Docker images compatible with your ledger version:
docker pull bcgovimages/aries-cloudagent:py36-1.16-1_0.7.5
3

Create Docker Network

Create a dedicated Docker network to isolate Indy services:
docker network create indy-network

Ledger Setup

1

Start Indy Ledger Nodes

Start the Indy ledger nodes:
cd von-network
./manage build
./manage start --logs
The ledger should be accessible at http://localhost:9000.
2

Validate Ledger Health

Check the ledger status through the web interface:
curl http://localhost:9000/status
Ensure all nodes show “Active” status.
3

Register Issuer DID

Register the issuer’s DID with write permissions. Access the web UI at http://localhost:9000 and:
  • Enter a seed (32 characters, e.g., 000000000000000000000000Issuer01)
  • Select “Endorser” role for write permissions
  • Submit to register the DID
Save the returned DID - you’ll need it for ACA-Py configuration.
For production deployments, use a properly governed Indy network with appropriate security controls. Local von-network is only suitable for development.

ACA-Py Agent Setup

CCDigital uses two ACA-Py agents:
  • Issuer Agent: Issues credentials to citizens
  • Holder Agent: Holds credentials and presents proofs during login
See the ACA-Py Agent Configuration guide for detailed setup.

Schema and Credential Definition

1

Create Schema

Create a credential schema on the issuer agent. The schema defines the attributes for citizen identity:
curl -X POST http://localhost:8021/schemas \
  -H "Content-Type: application/json" \
  -d '{
    "schema_name": "citizen_identity",
    "schema_version": "1.0",
    "attributes": ["id_type", "id_number", "first_name", "last_name", "email"]
  }'
Save the returned schema_id.
2

Create Credential Definition

Create a credential definition based on the schema:
curl -X POST http://localhost:8021/credential-definitions \
  -H "Content-Type: application/json" \
  -d '{
    "schema_id": "<SCHEMA_ID>",
    "tag": "default",
    "support_revocation": false
  }'
Save the returned credential_definition_id.
3

Configure CCDigital

Set the credential definition ID in your environment:
export INDY_CRED_DEF_ID="<CRED_DEF_ID>"
export ACAPY_CRED_DEF_ID="<CRED_DEF_ID>"

Connection Establishment

1

Generate Invitation from Issuer

Create an invitation from the issuer agent:
curl -X POST http://localhost:8021/connections/create-invitation \
  -H "Content-Type: application/json" \
  -d '{
    "my_label": "CCDigital Issuer"
  }'
Copy the invitation_url or invitation object from the response.
2

Accept Invitation from Holder

Accept the invitation on the holder agent:
curl -X POST http://localhost:8031/connections/receive-invitation \
  -H "Content-Type: application/json" \
  -d '<INVITATION_OBJECT>'
3

Confirm Active Connection

Verify the connection is in “active” state on both agents:
# Check issuer
curl http://localhost:8021/connections

# Check holder
curl http://localhost:8031/connections
Both should show "state": "active".

Credential Issuance

Python Issuance Script

CCDigital uses a Python script to issue credentials from the database. Configure the script location:
export INDY_TOOLS_WORKDIR="/path/to/indy-tools"
export INDY_VENV_ACTIVATE="source venv/bin/activate"
export INDY_SCRIPT="issue_credentials_from_db.py"

Issue Credentials

Run the issuance script to create credentials for eligible users:
cd $INDY_TOOLS_WORKDIR
source venv/bin/activate
python issue_credentials_from_db.py
The script queries the database for users and issues credentials via the ACA-Py Admin API.

Manual Credential Issuance

For testing, you can manually issue a credential:
curl -X POST http://localhost:8021/issue-credential-2.0/send \
  -H "Content-Type: application/json" \
  -d '{
    "connection_id": "<CONNECTION_ID>",
    "filter": {
      "indy": {
        "cred_def_id": "<CRED_DEF_ID>"
      }
    },
    "credential_preview": {
      "@type": "issue-credential/2.0/credential-preview",
      "attributes": [
        {"name": "id_type", "value": "CC"},
        {"name": "id_number", "value": "123456"},
        {"name": "first_name", "value": "Juan"},
        {"name": "last_name", "value": "Perez"},
        {"name": "email", "value": "[email protected]"}
      ]
    }
  }'

Login Flow with Present-Proof

CCDigital implements a verifiable credential login flow using the Present Proof 2.0 protocol.

Flow Overview

  1. User initiates login at /login/user
  2. Backend creates a proof request via IndyProofLoginService
  3. User’s wallet receives the proof request
  4. User presents proof using their credential
  5. Backend verifies the proof and extracts attributes
  6. User completes second factor (TOTP or email OTP)

Service Implementation

The login flow is implemented in:
  • IndyProofLoginService (src/main/java/co/edu/unbosque/ccdigital/service/IndyProofLoginService.java): Manages proof requests
  • UserAuthFlowService (src/main/java/co/edu/unbosque/ccdigital/service/UserAuthFlowService.java): Orchestrates authentication
  • IndyAdminClient (src/main/java/co/edu/unbosque/ccdigital/service/IndyAdminClient.java:30): HTTP client for ACA-Py Admin API

Extracted Attributes

After successful proof verification, CCDigital extracts:
  • id_type: Document type (CC, TI, etc.)
  • id_number: Identification number
  • first_name: First name
  • last_name: Last name
  • email: Email address

Access State Synchronization

Overview

CCDigital synchronizes user access states (ENABLED/SUSPENDED/DISABLED) to ACA-Py connection metadata. This allows the system to enforce access control during authentication.

Configuration

# Enable synchronization
export INDY_USER_ACCESS_SYNC_ENABLED="true"

# Metadata path template ({conn_id} is replaced at runtime)
export INDY_USER_ACCESS_SYNC_PATH="/connections/{conn_id}/metadata"

Implementation

The UserAccessGovernanceService handles synchronization:
// Update user access state and sync to Indy
userAccessGovernanceService.updateAccessState(userId, "SUSPENDED");
This updates both MySQL and the ACA-Py connection metadata.

Environment Variables

Configure the following environment variables for Indy/ACA-Py integration:
# Issuer agent admin URL
export INDY_ISSUER_ADMIN_URL="http://localhost:8021"

# Holder agent admin URL
export INDY_HOLDER_ADMIN_URL="http://localhost:8031"

# Connection ID (use 'auto' to resolve by label)
export INDY_HOLDER_CONNECTION_ID="auto"

# Holder agent label for auto-resolution
export INDY_HOLDER_LABEL="CCDigital Holder"

# Credential definition ID
export INDY_CRED_DEF_ID="<CRED_DEF_ID>"

# Admin API key (if agents require authentication)
export INDY_ADMIN_API_KEY="your-api-key"

# Access state synchronization
export INDY_USER_ACCESS_SYNC_ENABLED="true"
export INDY_USER_ACCESS_SYNC_PATH="/connections/{conn_id}/metadata"

# Proof polling configuration
export ACAPY_PROOF_POLL_INTERVAL_MS="1000"
export ACAPY_PROOF_POLL_TIMEOUT_MS="20000"
These are mapped in application.properties under ccdigital.indy.* and acapy.* prefixes.

Testing the Integration

Verify Proof Request

Test the proof request flow:
curl -X POST http://localhost:8021/present-proof-2.0/send-request \
  -H "Content-Type: application/json" \
  -d '{
    "connection_id": "<CONNECTION_ID>",
    "presentation_request": {
      "indy": {
        "name": "citizen_identity_proof",
        "version": "1.0",
        "requested_attributes": {
          "id_info": {
            "names": ["id_type", "id_number", "first_name", "last_name", "email"],
            "restrictions": [{"cred_def_id": "<CRED_DEF_ID>"}]
          }
        },
        "requested_predicates": {}
      }
    }
  }'

Check Presentation State

curl http://localhost:8021/present-proof-2.0/records/<PRES_EX_ID>

Review Agent Logs

# Issuer agent logs
docker logs <issuer-container-id>

# Holder agent logs
docker logs <holder-container-id>

Troubleshooting

Connection Not Active

If connections remain in “invitation” or “request” state:
  1. Check that both agents can reach each other’s endpoints
  2. Verify firewall rules allow traffic
  3. Check agent logs for connection errors

Credential Issuance Fails

Common issues:
  • Incorrect cred_def_id
  • Schema attributes don’t match credential preview
  • Connection not in “active” state
  • Insufficient ledger write permissions

Proof Verification Fails

Check:
  • Credential was successfully issued and stored
  • Proof request attributes match credential schema
  • Connection is active
  • Credential definition ID matches
Always verify that the credential definition ID in your proof request matches the one used during credential issuance.

Service References

  • IndyAdminClient (src/main/java/co/edu/unbosque/ccdigital/service/IndyAdminClient.java:30): HTTP client for ACA-Py Admin API
  • IndyProofLoginService (src/main/java/co/edu/unbosque/ccdigital/service/IndyProofLoginService.java): Proof request management
  • UserAuthFlowService (src/main/java/co/edu/unbosque/ccdigital/service/UserAuthFlowService.java): Authentication orchestration
  • UserAccessGovernanceService (src/main/java/co/edu/unbosque/ccdigital/service/UserAccessGovernanceService.java): Access state synchronization
  • IndyProperties (src/main/java/co/edu/unbosque/ccdigital/config/IndyProperties.java): Configuration properties

Next Steps

ACA-Py Agent Configuration

Detailed configuration for issuer and holder agents

Hyperledger Fabric Setup

Set up Fabric for document traceability

Build docs developers (and LLMs) love