Verification Engine — Service Reference
Module: backends/verification-engine · REST: 8082 · gRPC: 50052 · Stateless — no
local mutable state, no database of its own.
RPC surface (verification.proto)
verifyCredential(VerifyCredentialRequest) -> VerifyCredentialResponse
message VerifyCredentialRequest {
bytes credential_json = 1;
bool check_status = 2;
bool check_expiration = 3;
}
message VerifyCredentialResponse {
bool is_valid = 1;
string signature_status = 2; // "VALID", "INVALID", "UNVERIFIABLE"
string status_check_result = 3; // "ACTIVE", "REVOKED", "UNKNOWN"
string expiration_status = 4; // "VALID", "EXPIRED"
string issuer_did = 5;
int64 verified_at = 6;
repeated VerificationError errors = 7;
}Errors: INVALID_ARGUMENT (missing/invalid credential JSON), NOT_FOUND (issuer not in
registry), INTERNAL (unexpected error).
validateSignature(ValidateSignatureRequest) -> ValidateSignatureResponse
message ValidateSignatureRequest {
bytes credential_json = 1;
string issuer_did = 2;
bytes public_key = 3; // optional — resolved from registry if absent
}
message ValidateSignatureResponse {
bool is_valid = 1;
string algorithm = 2; // "eddsa-jcs-2022"
string verification_method = 3;
string error_message = 4;
}Targeted signature validation without full credential context — simple pass/fail.
checkCredentialStatus(CheckStatusRequest) -> CheckStatusResponse
message CheckStatusRequest {
string credential_id = 1;
string status_list_url = 2; // accepted, not yet consulted — see note below
}
message CheckStatusResponse {
string credential_id = 1;
string status = 2; // "ACTIVE", "DISPUTED", "REVOKED", "UNKNOWN"
int64 checked_at = 3;
string error_message = 4;
string superseded_by_credential_id = 5; // final current successor; empty if none
string superseded_by_issuer_name = 6;
string superseded_by_provenance_tier = 7;
string superseded_by_shareable_id = 8;
}Checks two independent Redis caches (verification:status:v2:, terminal-revocation, 30s TTL; and
verification:supersession:v1:, configurable via verification.cache.supersession-ttl-seconds,
300s default). On either cache miss, makes one Credential Issuance getCredential gRPC call
for both status and the final successor summary. An unresolved credential or successor chain fails
closed for successor disclosure.
status_list_urlis accepted (and threaded through Core Engine’s/api/v1/credentials/verify) but not yet consulted — every credential uses the simpleACTIVE/REVOKEDstatus column, not a W3C StatusList2021-style external bitstring. A caller can safely omit the field.
Supporting components
| Component | Role |
|---|---|
VerificationService.verifySignature() | Ed25519 verification via Bouncy Castle |
VerificationService.isNotExpired() | Expiration check |
VerificationService.checkCredentialStatus() | Reads status + successor disclosure from one Credential Issuance response, TTL-cached |
GrpcServerConfig | Netty server on 50052; registryStub() blocking stub to Issuer Registry (localhost:50051 default, 10 MB max, 30s keep-alive) |
GrpcLogInterceptor | Request/response logging, latency tracking, correlation ID via MDC |
grpc:
server:
port: 50052
enable-keep-alive: true
keep-alive-time: 30s
max-inbound-message-size: 10485760
issuer-registry:
grpc:
host: localhost # "issuer-registry" in Docker
port: 50051Test coverage
Reject empty credential JSON
Verify valid credential with signature
Check revocation status
Validate Ed25519 signature
Reject missing issuerAll 5 tests use CountDownLatch for async synchronization.
Stateless design
- No local mutable state — every data point fetched on-demand from the request or an external service.
- Enables horizontal scaling without session affinity; each request is independent and atomic.
Security
- Never handles private keys — all signing happens in Issuer Registry (or the BYOK issuer’s own custody).
- Public keys fetched on-demand from Issuer Registry, never cached locally except short-term Redis caching of verification results.
- Ed25519 only, 32-byte key length validated.
Manual testing
grpcurl -plaintext localhost:50052 list
grpcurl -plaintext -d '{"credential_json": "...base64-encoded-json..."}' \
localhost:50052 \
com.attestpro.verification.VerificationEngineService/VerifyCredentialWhat’s not implemented
Credential result caching beyond the two status/supersession caches, a batch verification RPC, webhook notifications on critical failures, and fetching/caching external revocation lists (BitString StatusList2021Entry).