Skip to content

Latest commit

 

History

1,934 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Status List Server

CI CD dependencies License License

The Status List Server manages and publishes status lists for credential issuers.
It allows issuers to register, publish, and update status lists, and verifiers to retrieve and validate them securely.

Overview

This service implements the Token Status List specification.
It supports both JWT and CWT formats, with cryptographic signing using multiple algorithms (ECDSA, EdDSA, RSA with SHA-256, SHA-384, SHA-512 digest algorithms).

For a detailed explanation of the architecture, see the hexagonal architecture documentation.

Feature Implementation Status

Feature Status Notes
Issuer Registration ✅ Implemented Public key registration via POST /api/v1/credentials
Status List Publishing ✅ Implemented JWT-signed publishing via PUT /api/v1/status-lists/{id}/statuses
Status List Updates ✅ Implemented Partial updates via PATCH /api/v1/status-lists/{id}/statuses
JWT Status List Format ✅ Implemented JWS Compact Serialization with exp/ttl
CWT Status List Format ✅ Implemented COSE_Sign1_Tagged (tag 18) with exp/ttl
Gzip Compression ✅ Implemented Applied to JWT responses only (draft-21 section 8.2)
HTTP Content Negotiation ⚠️ Partial Exact match only; RFC 9110 patterns (*/*, q=) not yet supported
Historical Resolution (time=) ✅ Implemented Optional feature for time-based status queries
Status List Aggregation ✅ Implemented GET /api/v1/aggregation + optional aggregation_uri token member
X.509 Certificate EKU ⚠️ Partial Placeholder OID (...3.30); rename pending spec finalization

Quick Start

Prerequisites

Before running the server, ensure you have the following tools installed:

  • Rust & Cargo (Latest stable).
  • A supported database backend: PostgreSQL, MySQL, or SQLite for persistent deployments. The default local mode uses in-memory repositories.
  • Docker (optional, for local testing).

Run locally

Clone the Repository:

git clone https://github.com/adorsys/status-list-server.git
cd status-list-server

Environment Variables:

Create a .env file in the root directory. Take a look at the .env.template file for an example of the required variables.

Running with Docker Compose

The simplest way to run the project is with docker compose:

  • Execute the command below at the root of the project:
docker compose up --build

This command will pull all required images and start the server compiled with default compose features (postgres,aws).

To pass custom Cargo feature flags during build, specify the FEATURES environment variable:

FEATURES="mysql,aws" docker compose --profile mysql up --build

Running Manually

To start the server in zero-infrastructure default in-memory mode, execute:

cargo run

By default, the server will listen on http://localhost:8000 using in-memory repositories, Moka cache, and store-based certificate management. No external databases or cloud services are required for development.

Cargo Feature Matrix

The crate uses modular Cargo feature flags to gate optional production backend drivers:

Feature Description Default
memory In-memory repositories, TTL cache, and static certificate loading. ✅ Default
postgres SeaORM PostgreSQL database driver. ❌ Opt-in
sqlite SeaORM SQLite database driver. ❌ Opt-in
mysql SeaORM MySQL database driver. ❌ Opt-in
aws AWS Secrets Manager and Route53 DNS-01 driver ❌ Opt-in
gcp GCP Secret Manager and Cloud DNS driver ❌ Opt-in
azure Azure Key Vault and Azure DNS driver ❌ Opt-in
vault HashiCorp Vault / OpenBao driver ❌ Opt-in

To build with specific backend drivers, pass the matching feature flag(s):

# Run with PostgreSQL support available
cargo run --features postgres

# Run with AWS native integration (automatically enables ACME)
cargo run --features postgres,aws

For deployment guidance and backend tradeoffs, see docs/database-backends.md.

API Documentation

The public API is documented with an OpenAPI 3.1 specification. See docs/openapi.yaml for the complete API contract.

Retrieve Status List Aggregation

  • Endpoint: GET /api/v1/aggregation

  • Description: Returns all Status List Token URIs hosted by this server in a single response (Token Status List draft-21 §9), enabling consumers to pre-fetch or keep an offline mirror of every list. The endpoint is publicly accessible with no authentication required. The aggregation is issuer-agnostic — every hosted status list URI is included regardless of which issuer owns it.

  • Responses:

    • 200 OK
    {
      "status_lists": [
        "https://statuslist.example.com/api/v1/status-lists/30202cc6-1e3f-4479-a567-74e86ad73693",
        "https://statuslist.example.com/api/v1/status-lists/755a0cf7-8289-4f65-9d24-0e01be92f4a6"
      ]
    }
    • 500 INTERNAL SERVER ERROR: System incurred an error

When the optional APP_SERVER__AGGREGATION_URI configuration is set, every emitted Status List Token (JWT and CWT) includes it as the optional aggregation_uri member (draft-21 §4.2 / §4.3), allowing a consumer to discover the aggregation link directly from any single list token. When unset, the member is omitted entirely.

Configuration

All runtime behavior is controlled via environment variables prefixed with APP_ and using __ as a nested separator (e.g. APP_SERVER__PORT=8000). Sensible defaults are built in, so only non-default values need to be set. See .env.template for a complete example.

For deployment guidance and backend tradeoffs, see docs/database-backends.md.

Validation

The following constraints are validated at startup and will cause the server to fail fast if violated:

  • server.port must be between 1 and 65535 (the u16 type enforces the upper bound)
  • server.cert.renewal_cron_schedule must be a valid 6-field cron expression (seconds required)

Security

Authentication

The server uses JWT-based authentication with the following requirements:

  1. Issuers must provide valid public key during registration using the /api/v1/credentials endpoint

  2. All authenticated requests must include a JWT token in the Authorization header:

    Authorization: Bearer <jwt_token>
  3. The JWT token must:

    • Be signed with the private key corresponding to the registered public key
    • Have iss (issuer) claim matching the registered issuer
    • Have valid exp (expiration) and iat (issued at) claims

Example JWT token header:

{
  "alg": "ES256"
}

Example JWT token claims:

{
  "iss": "test-issuer",
  "exp": 1752515200,
  "iat": 1752515200
}

Certificate Provisioning and Renewal

The Status List Server is provisioned with a cryptographic certificate that is embedded into all issued status list tokens. This certificate ensures the authenticity and integrity of the tokens distributed by the server.

  • Certificate issuance and renewal are managed according to the configured renewal strategy.
  • Every day, a cron job checks whether the certificate should be renewed based on this strategy.
  • If the certificate is still considered valid according to the configured strategy, no renewal occurs; renewal is only triggered when necessary.
  • The server signing key and certificate chain are stored in one cryptographic-material backend selected by enabled Cargo features (vault, aws, gcp, azure, or in-memory fallback).
  • Certificate material stays cached until provisioning or renewal invalidates it. Signing-key reads can be cached with APP_SERVER__CERT__SIGNING_KEY_CACHE_TTL; set it to 0 to force private-key reads to bypass the material cache.
  • Parsed certificate chains stay cached in memory until certificate provisioning or renewal replaces them.

Provisioning Modes:

  • ACME Provisioning: Requests, validates (via DNS-01 challenge), and automatically renews TLS certificates using Let's Encrypt / ACME directory. Built into cloud-native binary variants.
  • Static Loading: Loads pre-existing certificate chain and private key directly into memory, without ACME overhead or DNS challenges.
  • If neither or only partial static material is provided, server startup immediately fails with a clear validation error.

Certificate Manager Builder Defaults:

  • CertManager::builder() defaults to ACME provisioning.
  • The default renewal strategy is PercentageOfLifetime(None), which renews at 2/3 of the certificate lifetime.
  • ACME uses DefaultHttpClient unless .acme_http_client(...) is supplied.
  • Store provisioning does not create ACME HTTP client state unless explicitly configured.
  • email defaults to an empty string, organization defaults to none, and eku defaults to none.
  • domains and crypto_storage must always be provided.
  • ACME additionally requires challenge_handler and acme_directory_url.
let manager = CertManager::builder()
    .domains(["statuslist.example.com"])
    .email("support@example.com")
    .organization(Some("example.com"))
    .acme_directory_url("https://acme-v02.api.letsencrypt.org/directory")
    .crypto_storage(material_storage)
    .challenge_handler(challenge_handler)
    .eku(&[1, 3, 6, 1, 5, 5, 7, 3, 30])
    .acme_strategy()
    .build()?;
let manager = CertManager::builder()
    .domains(["statuslist.example.com"])
    .crypto_storage(material_storage)
    .store_strategy(StoreProvisioningStrategy::filesystem(
        "/etc/status-list/tls.crt",
        "/etc/status-list/tls.key",
    ))
    .build()?;

DNS Providers:

ACME DNS-01 challenges are solved through a configurable DNS provider. AWS Route53, Cloudflare, Google Cloud DNS, Azure DNS and self-hosted ACME-DNS are supported, selected via APP_SERVER__CERT__DNS__PROVIDER. See the DNS Provider Documentation for setup instructions.

Error Handling

The server implements proper error handling and returns appropriate HTTP status codes:

  • 400 BAD REQUEST: Invalid input data
  • 401 UNAUTHORIZED: Missing or invalid authentication token
  • 403 FORBIDDEN: Insufficient permissions
  • 404 NOT FOUND: Resource not found
  • 406 NOT ACCEPTABLE: Requested format not supported
  • 409 CONFLICT: Resource already exists
  • 500 INTERNAL SERVER ERROR: Server-side error

Deployment

The server can be deployed using a containerization platform such as Docker.

Helm Chart Deployment

A Helm chart is provided for easy deployment on Kubernetes. For detailed instructions, see the Helm Deployment Guide.

Container Supply Chain

Release images are scanned by digest and carry an SBOM and SLSA provenance, and release tags are applied only after the scan. See the Container Supply Chain guide for thresholds, verification commands, and how to triage findings.

Testing

You can run the tests using the following command:

cargo test

To verify the infrastructure-free application composition (domain models, domain ports, service container, and in-memory outbound adapters only), run:

cargo check --no-default-features --features memory

Contributing

Contributions are welcome and encouraged. Before contributing, please review the architecture documentation, which provides an overview of our architectural design. Also refer to the contributing guide for more details.

Releases

This project uses release-plz with Conventional Commits to automate versioning and changelog generation. Every push to main opens or updates a Release PR. Merging that PR creates a git tag and a GitHub Release automatically. The first Release PR creates the initial changelog entry.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

status list server

Topics

Resources

Contributing

Stars

22 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages