status-list-go

A Go-based revocation project for Attestation Status List and Attestation Revocation List.
Description
github.com/unknovs/status-list-go is a Go service designed to issue and manage revocation status using Attestation Status Lists and Attestation Revocation Lists.
Cryptographic Keys and Certificates
Requirements
The Status List service requires ECDSA P-256 (prime256v1) cryptographic keys and certificates for JWT signing. The service supports both development and production deployment scenarios.
Private Keys:
Certificates:
- DER format (binary)
- PEM format (base64-encoded)
Configuration
The service uses environment variables for flexible certificate management:
# Certificate paths (can be absolute or relative)
PRIVATE_KEY_PATH=/path/to/private-key.pem
CERTIFICATE_PATH=/path/to/certificate.der
COUNTRY_CODE=LV
# API configuration
API_KEY=your-api-key
SERVICE_URL=http://localhost:8080/
# Service Mode (new in v1.x)
SERVICE_MODE=internal # "public" or "internal" (default: "internal")
# - "public": Read-only endpoints (/get, /{country}/{doctype}/{id}), no Swagger
# - "internal": Full API including /take, /set (API key protected), with Swagger
Development Setup
For development, you can generate test certificates:
# Generate a private key
openssl ecparam -genkey -name prime256v1 -out private-key.pem
# Generate a self-signed certificate
openssl req -new -x509 -key private-key.pem -out certificate.pem -days 365
# Convert certificate to DER format (optional)
openssl x509 -in certificate.pem -outform DER -out certificate.der
Production Deployment with Docker Secrets
For production, use Docker secrets or mounted volumes:
# Using Docker secrets
echo "your-private-key-content" | docker secret create private_key -
echo "your-certificate-content" | docker secret create certificate -
docker run -d \
--secret private_key \
--secret certificate \
-e PRIVATE_KEY_PATH=/run/secrets/private_key \
-e CERTIFICATE_PATH=/run/secrets/certificate \
-e COUNTRY_CODE=LV \
-e API_KEY=your-production-api-key \
your-statuslist-service
Key Conversion
If you have an encrypted PKCS#8 private key, you can convert it to an unencrypted format:
# Convert encrypted PKCS#8 to unencrypted PEM
openssl pkcs8 -in encrypted-key.pem -out decrypted-key.pem -passin pass:your-password
# Verify the key format
openssl pkey -in decrypted-key.pem -text -noout
Security Considerations
- Production: Always use properly issued certificates from a trusted CA
- Key Protection: Keep private keys secure and use appropriate file permissions (600)
- Encryption: For production, consider using encrypted keys with secure password management
- Rotation: Implement regular key rotation procedures
- Validation: Ensure certificates have proper key usage extensions for digital signatures
Troubleshooting
Common Issues:
-
"failed to parse private key" errors:
- Ensure your private key is in ECDSA P-256 format
- Convert encrypted PKCS#8 keys using the conversion command above
-
"ASN.1 structure error" messages:
- This typically indicates an encrypted key that couldn't be decrypted
- Verify the password is correct
- Convert to unencrypted PEM format for development
-
"Certificate loading failed":
- Verify the certificate file exists and is readable
- Ensure the certificate matches your private key
- Both DER and PEM formats are supported
-
JWT signing failures:
- Confirm the private key and certificate are from the same key pair
- Verify the certificate is valid and not expired
Storage Backend Configuration
The Status List service supports pluggable storage backends for horizontal scaling and flexibility.
Available Storage Backends
1. Local Filesystem (Default)
The default storage backend uses the local filesystem. This is suitable for:
- Development and testing
- Single-instance deployments
- Environments where shared storage is not required
Configuration:
# Local storage is the default - no additional configuration needed
STATUS_LIST_STORAGE=local # Optional, defaults to "local"
STATUS_LIST_DIR=/var/opt/status_lists
BACKUP_DIR=/var/opt/status_list_backup
LOG_DIR=/tmp/status_lists
2. S3 / S3-Compatible Storage
For production deployments requiring horizontal scaling with multiple service instances, use S3 or S3-compatible storage (MinIO, LocalStack, etc.).
Configuration:
# S3 Storage Backend
STATUS_LIST_STORAGE=s3
S3_BUCKET=status-lists
S3_REGION=us-east-1 # Optional for S3-compatible services
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_ENDPOINT=http://localhost:9000 # Optional: for MinIO or other S3-compatible services
AWS S3 Example:
STATUS_LIST_STORAGE=s3
S3_BUCKET=my-status-lists-bucket
S3_REGION=eu-west-1
S3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
S3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
MinIO Example (Local Development):
STATUS_LIST_STORAGE=s3
S3_BUCKET=status-lists
S3_ENDPOINT=http://minio:9000
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_REGION=us-east-1
Docker Compose with S3 Storage
The included docker-compose.yml provides a complete setup with MinIO for local S3 testing:
# Start with MinIO S3 storage
docker-compose up -d
# The minio-init service automatically creates the required bucket
# MinIO console available at: http://localhost:9001
To enable S3 storage in the service, uncomment the S3 environment variables in docker-compose.yml:
environment:
STATUS_LIST_STORAGE: "s3"
S3_BUCKET: "status-lists"
S3_ENDPOINT: "http://minio:9000"
S3_ACCESS_KEY_ID: "minioadmin"
S3_SECRET_ACCESS_KEY: "minioadmin"
S3_REGION: "us-east-1"
Storage Features
- Optimistic Locking: Version-based concurrency control prevents data corruption
- Atomic Operations: All write operations are atomic (local: temp file + rename, S3: object metadata versioning)
- Automatic Retry: AWS SDK built-in exponential backoff for transient failures
- Connection Validation: S3 bucket accessibility is validated at startup
Multi-Instance Deployment
When using S3 storage, multiple service instances can share the same bucket:
# Instance A
docker run -d -p 8080:8080 \
-e STATUS_LIST_STORAGE=s3 \
-e S3_BUCKET=shared-status-lists \
statuslist-service
# Instance B (different port)
docker run -d -p 8081:8080 \
-e STATUS_LIST_STORAGE=s3 \
-e S3_BUCKET=shared-status-lists \
statuslist-service
Both instances will read and write to the same S3 bucket, enabling horizontal scaling.
Service Mode: Public vs Internal
The service supports two operational modes via the SERVICE_MODE environment variable, allowing you to run separate instances for public and internal use:
Public Mode (SERVICE_MODE=public)
Purpose: Internet-facing, read-only access to status lists
Features:
- ✅ GET endpoints only (no write operations)
- ✅
/token_status_list/get - Query status by URI and index
- ✅
/token_status_list/{country}/{doctype}/{id} - Serve status list
- ✅
/health - Health check endpoint
- ❌ No Swagger UI (security)
- ❌ No
/take endpoint (no status list creation)
- ❌ No
/set endpoint (no status updates)
Use Case: Load-balanced public instances for verifying credential status
Internal Mode (SERVICE_MODE=internal)
Purpose: Protected API for credential issuers and administrative operations
Features:
- ✅ All GET endpoints (read operations)
- ✅ POST
/token_status_list/take - Create/allocate status list entries (API key required)
- ✅ POST
/token_status_list/set - Update status entries (API key required)
- ✅ Swagger UI at
/token_status_list/swagger
- ✅ Full API documentation and testing interface
Use Case: Single protected instance for credential issuance systems
Deployment Example: Public + Internal
# Internal instance (protected, single instance)
docker run -d \
--name statuslist-internal \
-p 8080:8080 \
-e SERVICE_MODE=internal \
-e API_KEY=secret-api-key \
-e STATUS_LIST_STORAGE=s3 \
-e S3_BUCKET=status-lists \
statuslist-service
# Public instances (scalable, no API key needed)
docker run -d \
--name statuslist-public-1 \
-p 8081:8080 \
-e SERVICE_MODE=public \
-e STATUS_LIST_STORAGE=s3 \
-e S3_BUCKET=status-lists \
statuslist-service
docker run -d \
--name statuslist-public-2 \
-p 8082:8080 \
-e SERVICE_MODE=public \
-e STATUS_LIST_STORAGE=s3 \
-e S3_BUCKET=status-lists \
statuslist-service
Architecture Benefits
- Security Isolation: Write operations isolated from public internet
- Independent Scaling: Scale public read instances horizontally based on verification load
- Simplified Access Control: No API key management on public instances
- Reduced Attack Surface: Swagger and write endpoints not exposed publicly
- Cost Optimization: Minimal resources for internal instance, scale only public as needed
Network Configuration
Typical Setup:
Internet → Load Balancer → Public Instances (SERVICE_MODE=public)
↓ (read from)
S3 Bucket
↑ (write to)
Internal Network → Internal Instance (SERVICE_MODE=internal)
Swagger
HOST{/token_status_list/swagger}
Features
- Supports Attestation Status List (ASL) draft-ietf-oauth-status-list-12
in both JWT (
statuslist+jwt) and CWT (application/statuslist+cwt) formats.
- The CWT format is now IETF draft-ietf-oauth-status-list-12 conformant (see
changelog.md).
- Both formats are continuously verified against the EU reference verifier library
github.com/gmb-eudi/go-statuslist
in the test suite (services.TestIssuedTokensVerifyWithGoStatuslist), which fails the
build on any wire-format drift.
- Supports Attestation Revocation List (ARL) [ ISO/IEC CD 18013-5 second edition ]
- The identifier-list mechanism is not yet aligned to draft-12 (pending the ARF VCR_11 Commission TS).
How to contribute
We welcome contributions to this project. To ensure that the process is smooth for everyone
involved, follow the guidelines found in CONTRIBUTING.md.
License
License details
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.