Issuer practice statement · v1.3.1
The operational truth about firmas's credential infrastructure. Describes what is in production today, not what is aspirational. Where a control is not yet implemented, the section says so explicitly.
| Document identifier | firmas-issuer-practice-statement.html |
|---|---|
| Document version | 1.3 |
| Published | 2026-05-25 (v1.0) — see Document control footer for revision history |
| Authoritative URL | https://www.firmas.io/.well-known/firmas-issuer-practice-statement.html |
| Authoritative language | English |
| Legal entity | Rindogatan LLC |
|---|---|
| Issuer DNS name | https://www.firmas.io |
| Issuer key ID (kid) | firmas-vouch-2026 |
| Public JWKS endpoint | /.well-known/firmas-vouch-jwks.json |
| Federation entity statement | /.well-known/openid-federation |
| jwt-vc-issuer metadata | /api/vouch/jwt-vc-issuer |
| OpenID4VCI issuer metadata | /.well-known/openid-credential-issuer |
| OpenID4VP relying party metadata | /.well-known/openid-relying-party |
| Scope of attestation | Attestation scope |
| Contact | firmasfb@rindogatan.com |
firmas issues Electronic Attestations of Attributes (EAA) as a non-qualified trust service provider. The credentials it issues do not meet the qualified-tier requirements set out in Article 45d of Regulation (EU) No 910/2014 (as amended by Regulation (EU) 2024/1183, “eIDAS 2.0”), and rely on Article 45b of the same Regulation for legal effect — that article provides that an EAA shall not be denied legal effect or admissibility as evidence in legal proceedings on the sole ground that it is in electronic form or that it does not meet the qualified-tier requirements. This statement applies to all credentials issued and revoked under the firmas-vouch-2026 key identifier. firmas is not a Qualified Trust Service Provider (QTSP), is not listed on any EU Member State Trusted List (LOTL), and does not represent its services as equivalent to qualified trust services.
This statement does NOT cover: (a) the firmas mobile applications themselves, which are governed by their own privacy policy and terms; (b) the Dealroom internal cross-application signing flow, which uses the Identity (Name) credential type that is not publicly issued (see §6.7.4); (c) any third-party verifier's downstream use of firmas-issued credentials.
docs/regulatory/disaster-recovery-procedure.md in the source repositorydocs/regulatory/records-archival-policy.md in the source repositorydocs/regulatory/kms-migration-plan.md in the source repositorydocs/regulatory/etsi-119-471-gap-analysis.md in the source repositoryTo be drafted with legal counsel. Per the ETSI 119 471 §6 template, this section enumerates the obligations of the TSP, the subscriber (credential holder), and the relying party, and declares the liability framework. firmas's posture is non-qualified — the credentials do not meet the Article 45d qualified-tier requirements and rely on the Article 45b legal-effect safe-harbor. Liability is governed by Article 13 of Regulation (EU) 2024/1183. The substantive drafting of this section is Phase B of the bridging plan in docs/regulatory/etsi-119-471-gap-analysis.md and requires counsel familiar with eIDAS 2.0 + Spanish national implementation.
The honest summary in the meantime: firmas does not warrant fitness for any particular purpose; relying parties verify credentials against the published JWKS at their own risk; subscribers consent to the device-local evidence aggregation by installing the firmas application and crossing the trait threshold; the trust framework's substantive ceiling is what is described in this practice statement plus the attestation scope document. Liability is not waived but is not separately enumerated here pending counsel review.
Issuance is device-local with the smallest possible server-side footprint. The user's device computes the eligibility score from local evidence (Vouch v3 on web; Vouch v2.5 on native — the document-only cap of 65 is uniform across both). When the score crosses the threshold of 75, the device requests issuance from firmas by POSTing only:
human, over_18, residency);assurance_evidence object carrying named anti-forgery controls — see §6.7.2.firmas's server never sees the holder's declared name, ID number, document portfolio, OCR matches, or handshake counterparty identities. The credential payload is constructed server-side from the trait identifier and the device public key only.
Every credential carries a holder-binding element so the verifier can confirm the presenter has the device key bound at issuance:
cnf.jwk per RFC 7800. Presentation-time authentication is via a Key Binding JWT (SD-JWT VC §4.3) carrying the request's nonce + aud + sd_hash signed by the holder's device key.deviceKeyInfo. Presentation-time authentication is via DeviceSigned per ISO 18013-5 §9.1.3, with the COSE_Sign1 payload bound to the OpenID4VPHandover SessionTranscript per ISO 18013-7 §B.4.4.The Identity (Name) credential follows a parallel path through /api/vouch/issue-identity-credential, which accepts the declared name and a salted SHA-256 of the ID number (the salt is derived from the device public key; the raw ID number never leaves the device). This path produces an SD-JWT VC consumable by Dealroom's cross-app signing flow. The /api/vouch/issue generic path explicitly rejects the identity trait with HTTP 403. The Identity credential is internal-only — not advertised in OpenID4VCI issuer metadata, not requestable through the public OpenID4VP responder, not visible in the firmas widget.
See §6.3.1. firmas does not perform separate application processing — the credential request itself is the application, and acceptance is automatic upon threshold crossing + signature.
The subscriber implicitly accepts the issued credential by installing it in their wallet. firmas does not require a separate signed acceptance statement; the holder-binding signature at presentation time evidences the subscriber's intent to use the credential.
Credentials are issued with the per-trait validity periods listed in §6.7.1. Holders may re-mint a credential at any time; the device cache will use the freshest cached credential whose exp is in the future. Past-exp credentials are rejected by the firmas verifier endpoint and by the firmas widget.
firmas implements the IETF Token Status List mechanism (draft-ietf-oauth-status-list-20):
status.status_list.{idx, uri} claim pointing at a published bitstring./api/vouch/status-list and at per-sub-list URIs under /api/vouch/status-list/{listId} with Content-Type: application/statuslist+jwt, refreshed every 12 hours./api/vouch/admin/revoke, gated by a constant-time-compared operator secret, flips the relevant bit on demand. Revocations propagate within the Status List Token's TTL.status claim; the verifier treats absence as "no revocation possible" and they live out their natural exp window.firmas does NOT implement suspension. A credential is either valid (status bit 0) or revoked (status bit 1); there is no intermediate state. A holder requesting temporary inactivation receives a recommendation to revoke and re-mint when desired; the revocation is irreversible at the Status List level but a fresh credential can always be issued.
firmas accepts OpenID4VCI 1.0 §10 lifecycle notifications from wallets at /api/vouch/oidc/notification: credential_accepted, credential_failure, credential_deleted. The endpoint requires the wallet to echo the notification_id issued at the credential step; strict correlation against the persisted value enforces Section 10.2 of the OID4VCI specification.
firmas operates as a cloud-hosted service on Vercel (production deployment, project iammurabi) backed by Supabase (database + service-role authentication). firmas does not operate its own physical infrastructure. The physical security of the underlying compute and storage is the responsibility of Vercel and Supabase respectively, governed by their published security policies and SOC 2 attestations.
firmas is currently operated as a single-operator service (the founder and one or two trusted contributors with administrative access). All operational secrets — including the issuer signing key (VOUCH_ISSUER_PRIVATE_KEY_JWK) and the administrative revocation token (VOUCH_ADMIN_SECRET) — live as encrypted environment variables on the Vercel production project, with access limited to operators with Vercel project ownership.
Every operationally-meaningful event is recorded in the vouch_audit_events table in Supabase. Six event types are captured: credential_issued, credential_revoked, notification_received, oid4vp_request_minted, oid4vp_response_received, admin_access. Each row carries the timestamp, the subject identifier (credential jti or request id), structured context, the actor (system, admin, or wallet), and the request IP + user-agent when an inbound HTTP request triggered the event.
The audit log is queryable by the operator via the gated endpoint at /api/vouch/admin/audit-log. Retention is 36 months per the records archival policy (see §6.5.5 and the records archival document referenced in §6.1.4).
The full retention matrix lives in docs/regulatory/records-archival-policy.md in the source repository. Summary:
| Class | Retention window |
|---|---|
Credential metadata (vouch_status_entries) | credential exp + 30 days |
| In-flight OIDC sessions | 15 minutes unconfirmed; per credential window if confirmed |
| OID4VP presentation requests | 30 days |
| Audit events | 36 months |
| Vouch v2 presentations | 24 hours |
| Vercel function logs (Vercel-managed) | 7 days at the Pro tier |
| Source repository | Indefinite |
Retention is enforced by a scheduled cron job at /api/cron/cleanup that runs daily at 03:23 UTC and prunes each class against the documented window.
The issuer signing key is currently held as an environment-variable-resident JSON Web Key. Migration to a hardware-security-module-backed module (recommendation: AWS KMS) is documented in docs/regulatory/kms-migration-plan.md. The migration plan covers cloud-provider selection, the KeyProvider abstraction, the cutover procedure (both old and new public keys live in the JWKS for the credential-lifetime overlap window), and the cost-of-non-action analysis.
Today key rotation is operationally feasible (generate new JWK, swap env var, redeploy, publish new key in JWKS) but lacks the FIPS 140-2 compliance level that ETSI 119 471 §6.5.5 and Kantara IAF §4.5.3 reference. This is the principal Phase C engineering item bridging v1.3 to v2.0.
The disaster recovery procedure with RTO/RPO targets, six failure modes, recovery procedures, drill schedule, and forward-looking improvements is documented in docs/regulatory/disaster-recovery-procedure.md. Headline targets:
Security incidents affecting firmas credentials or the issuer key should be reported to firmasfb@rindogatan.com. firmas commits to acknowledging reports within 72 hours and to publishing remediation timelines for any incident affecting issued credentials. The full incident response playbook lives within the disaster recovery procedure document referenced in §6.5.6; key-compromise specifically is covered as Mode 3 of that document.
If firmas terminates the trust service, it will (a) revoke all outstanding credentials via mass-revocation against the Status List, (b) publish a final practice statement amendment declaring termination, (c) notify any regulatory body to which a notification was previously filed (SETSI under Article 19a of Regulation 2024/1183, if applicable), and (d) maintain the JWKS endpoint and Status List for at least 90 days post-termination so verifiers can still resolve outstanding credentials' revocation state during their natural expiry window. The source repository remains public under Rindogatan LLC's ownership for archival.
| Layer | Algorithm |
|---|---|
| Issuer signature | ECDSA P-256 (ES256, RFC 7515) |
| Device key | ECDSA P-256 (generated on-device; iOS Secure Enclave, Android Keystore, Web non-extractable CryptoKey) |
| Holder binding (SD-JWT VC) | cnf.jwk per RFC 7800; Key Binding JWT at presentation |
| Holder binding (mDoc) | COSE_Key in MSO deviceKeyInfo; DeviceSigned COSE_Sign1 over OpenID4VPHandover SessionTranscript |
| Selective disclosure (SD-JWT VC) | IETF SD-JWT VC (draft-ietf-oauth-sd-jwt-vc); SD-JWT (RFC 9901) |
| Selective disclosure (mDoc) | SHA-256 digest map per ISO 18013-5 §9.1.2.5 |
| Hash | SHA-256 throughout (credential content, ID-number salt, blockchain anchor, mDoc digests) |
| Status List compression | zlib DEFLATE per draft-ietf-oauth-status-list-20 §4.1 |
| Presentation transport | OpenID4VP-aligned (firmas-native short-id relay plus OpenID4VP authorize/response endpoints plus the verifier-initiated ceremony at /api/vouch/oid4vp-verifier/*) |
The issuer key was generated off-line as a P-256 JWK and loaded into the production environment via the encrypted Vercel environment variable. Device keys are generated on the holder's device in the most-restrictive hardware-backed key store available on each platform (iOS Secure Enclave with biometric or device-unlock gating; Android Keystore hardware-backed where the device supports it; Web non-extractable CryptoKey persisted as a structured-clone in IndexedDB — the PKCS8 byte form never reaches disk).
Operational reality as of 2026-05-25: the web non-extractable migration shipped in Phase 1; this satisfies the "sole control" criterion of Article 26(c) of Regulation 2024/1183 for advanced electronic signatures on all three platforms.
Device private keys never leave the device's hardware-backed key store. Signing operations happen inside the keystore, with biometric or device-unlock gating per platform conventions.
Issuer private key protection is currently env-var-resident as described in §6.5.5. The KMS migration plan referenced there is the path to HSM-backed protection.
All firmas endpoints are served exclusively over HTTPS via Vercel's edge network. HSTS is enforced with strict-transport-security: max-age=63072000. The administrative endpoints (/api/vouch/admin/*) require constant-time-compared Bearer authentication against VOUCH_ADMIN_SECRET. The scheduled cleanup endpoint at /api/cron/cleanup is gated identically against CRON_SECRET.
firmas does NOT operate as a Qualified Trust Service Provider for time stamping under Article 41 of Regulation 2024/1183. The Base Mainnet anchor for in-person Karma handshakes provides tamper-evident proof of existence at a point in time but is explicitly not represented as a qualified electronic timestamp.
ETSI 119 471 §6.7 is titled "Certificate, CRL, and OCSP Profiles." firmas issues attribute attestations rather than X.509 certificates and uses the IETF Token Status List rather than CRL or OCSP. This section maps each ETSI requirement to the firmas equivalent.
| Type | Claim | Validity | Distribution |
|---|---|---|---|
| Human | The holder is a real person (peer-witnessed, probabilistic) | 365 days | Public (any OpenID4VP-aligned verifier; firmas widget) |
| Adult | The holder is 18 years of age or older | 365 days | Public |
| Resident of X | The holder resides in jurisdiction X within a freshness window | 180 days | Public |
| Identity (Name) | Declared legal name, signed for cross-app sign flows | 365 days | Internal-only — Dealroom cross-app signing only. Not offered through the generic Vouch share surface or the public widget. |
Every public-trait credential is issuable in two wire formats: SD-JWT VC (IETF draft) and ISO 18013-5 mDoc. Both formats are signed by the same ECDSA P-256 issuer key.
Every public-trait credential (Adult, Human, Resident) can optionally carry an assurance_evidence claim exposing the named evidence behind the attestation: vouch_score, os_age_signal, an array of anti_forgery_controls active for this user, handshake_count, document_count, document_age_days_p50, jurisdiction_match, and authoritative_source_present. The claim is selectively-disclosable per IETF SD-JWT — the verifier explicitly asks for it or sees only the digest. The intent is to give a verifier the substantiation behind a trust score (eIDAS Art. 45d / FTC §5), without forcing every relying party to consume it. When the issuance request does not supply evidence, the claim is omitted entirely.
firmas does NOT publish a CRL or operate an OCSP responder. Revocation is exclusively via the IETF Token Status List as detailed in §6.4.4. The bitstring profile follows draft-ietf-oauth-status-list-20 §4.1 (1 bit per credential, zlib-DEFLATE compressed, base64url-encoded). Sub-list partitioning is supported: each named list is served at its own URI under /api/vouch/status-list/{listId}.
Every credential vct URI resolves to a JSON metadata document per IETF draft-ietf-oauth-sd-jwt-vc. The metadata describes the credential name, description, per-claim display labels, selective-disclosure mode for each claim, and a schema_uri pointing at a JSON Schema document the wallet may use to validate the issued credential's structure:
https://firmas.io/credentials/age-attestation/v1 — Adult (18+) Attestationhttps://firmas.io/credentials/humanity-attestation/v1 — Humanity Attestationhttps://firmas.io/credentials/residency-attestation/v1 — Residency AttestationThe display copy is localized to seven languages (English, Spanish, Italian, German, Japanese, Indonesian, Brazilian Portuguese).
firmas exposes a public credential-verifier endpoint at POST /api/vouch/verify accepting both SD-JWT VC and mDoc credentials. The endpoint returns a structured verdict: signature against the issuer's resolved JWKS, expiry, Token Status List revocation, and (when a Key Binding JWT or DeviceSigned is supplied) holder binding. The OpenID4VP verifier-initiated ceremony at /api/vouch/oid4vp-verifier/* wraps this primitive in the request-object + presentation-definition + nonce flow so relying parties may pull selectively-disclosed presentations from wallets directly.
To be drafted with legal counsel. Per the ETSI 119 471 §6 template, this section enumerates the cadence of compliance audits, the identity of the auditor, the scope of audit, and the actions to be taken on findings. firmas's current state: no audit has been performed; a Kantara IAF conformance assessment is the most likely first audit (skeleton at docs/regulatory/kantara-application-skeleton.md). The substantive drafting of this section is Phase B of the bridging plan in docs/regulatory/etsi-119-471-gap-analysis.md and depends on the founder's decision to engage Kantara or equivalent assessor.
In the interim, the Vercel access logs, the structured audit log (§6.5.3), and the supervisory body inquiry path (SETSI under Article 19a; notification draft at docs/regulatory/setsi-art-19a-notification-draft.md) constitute the operative compliance posture.
To be drafted with legal counsel. Per the ETSI 119 471 §6 template, this section covers fees, financial responsibility, confidentiality of business information, intellectual property, representations and warranties, disclaimers, limitations of liability, indemnification, term and termination, governing law, and dispute resolution. firmas's public-facing legal terms (the Privacy Policy and Terms of Service at /privacy and /terms) cover the user-facing dimensions; this section's substantive drafting is Phase B of the bridging plan and requires counsel familiar with eIDAS 2.0 + Spanish national implementation + Rindogatan LLC's incorporated jurisdiction.
Governing law: pending counsel determination based on Rindogatan LLC's incorporation. firmas does not warrant fitness for any particular purpose and disclaims liability beyond what is statutorily imposed under Article 13 of Regulation (EU) 2024/1183 on non-qualified trust service providers.
firmas implements the OpenID4VCI 1.0 issuance protocol with the OpenID4VC High Assurance Interoperability Profile (HAIP) 1.0 strict requirements layered on top:
plain is rejected.jkt binding is enforced on every subsequent request.draft-ietf-oauth-attestation-based-client-auth is accepted on the PAR and Token endpoints (OAuth-Client-Attestation + OAuth-Client-Attestation-PoP headers). firmas verifies the JWT shape and the PoP signature; the outer attester signature is currently signal-only because no production wallet-provider trust list exists yet.require_pushed_authorization_requests: true is advertised in the issuer metadata.The full statement of what firmas does not attest to is in the attestation scope document. Summary:
firmas does not appear on any EU Member State Trusted List (LOTL). The LOTL is reserved for qualified Trust Service Providers; firmas is non-qualified by design. firmas's trust-publication strategy is three-pronged:
/.well-known/firmas-vouch-jwks.json and verify credentials offline./.well-known/openid-federation; embeds both the issuer (openid_credential_issuer) and the verifier (openid_credential_verifier / openid_relying_party) metadata blocks. Self-asserted root entity today.docs/regulatory/setsi-art-19a-notification-draft.md.This practice statement is a working document. Changes will be timestamped in the version history below; material changes will trigger a JWKS announcement and a notification at /business. Verifiers that depend on specific claims herein are encouraged to cache the URL and check for updates periodically.
| Version | Date | Change |
|---|---|---|
| v1.0 | 2026-05-25 | Initial publication |
| v1.1 | 2026-05-26 | Added §11 HAIP / OID4VCI conformance and §12 SD-JWT VC Type Metadata |
| v1.2 | 2026-05-27 (am) | Added §13 assurance_evidence and §14 verifier surface |
| v1.3 | 2026-05-27 (pm) | Restructured under ETSI TS 119 471 §6 template headings. Substance unchanged from v1.2; §6.2, §6.8, §6.9 stubbed pending counsel Phase B drafting. Audit log + DR + records archival + KMS migration referenced as supporting docs. |
| v1.3.1 | 2026-05-27 (eve) | Citation precision correction to §6.1.3 and the §6.2 stub: clarifying that Article 45b of Regulation (EU) No 910/2014 (as amended by 2024/1183) is the legal-effect safe-harbor (an EAA cannot be denied legal effect on the sole ground that it is electronic or non-qualified), while Article 45d defines the qualified-tier requirements that firmas does NOT meet. Substance and operational posture unchanged; previous wording implied Article 45b authorises non-qualified issuance, which is imprecise — the article protects legal effect rather than authorising the activity. |