Architektur
Architektur-Diagramme
Visualisierung der wichtigsten Flows: System-Topologie, License-Lifecycle, Heartbeat-Zyklus, Webhook-Zustellung und Schlüssel-Rotation.
System-Topologie
Wer spricht mit wem: Vendor-API, Customer-Verifikation, JWKS-Endpunkt, Webhook-Auslieferung, Persistenz.
graph TD
V["Vendor App<br/>Bearer xv_live_*"] -->|"POST /api/v1/vendor/licenses"| API["xcess.ch API<br/>Next.js + Prisma"]
API -->|"RS256, ES256 or EdDSA sign"| KP["Signing Key Provider<br/>Local or future HSM"]
API -->|"Persist"| DB[("PostgreSQL<br/>eu-central-2")]
API -->|"Audit and Heartbeat"| DB
V -.->|"Hand off license JWT"| C["Customer App<br/>any language"]
C -->|"GET JWKS, cached 1h"| JWKS["JWKS Endpoint"]
JWKS --> API
C -->|"Offline verify, jose or xcess SDK"| LV["Local JWT verification"]
API -->|"Webhook events, HMAC-SHA256"| W["Vendor Webhook<br/>Endpoint"]
API -->|"Heartbeat"| HB["POST /api/heartbeat"]
classDef brand fill:#1e1b4b,stroke:#1e1b4b,color:#fff;
classDef accent fill:#06b6d4,stroke:#06b6d4,color:#fff;
classDef store fill:#f1f5f9,stroke:#94a3b8;
class API,KP brand;
class JWKS,W accent;
class DB store;
License-Lifecycle
Vom POST des Vendors bis zur Offline-Verifikation beim Customer.
sequenceDiagram
autonumber
actor V as Vendor
participant X as xcess.ch API
participant DB as Postgres
actor C as Customer
V->>X: POST /api/v1/vendor/licenses with Bearer xv_live_*
X->>X: Validate input + per-tier quota
X->>DB: Insert License + Customer
X->>X: Sign JWT with RS256, ES256 or EdDSA
X-->>V: 201 returns licenseJwt + licenseNumber
V-->>C: Distribute JWT via file or API
C->>X: GET /.well-known/jwks/code, cached 1h
X-->>C: returns keys array
C->>C: Verify JWT offline using xcess SDK or jose
C->>X: POST /api/heartbeat optional
X->>DB: record Heartbeat
X-->>C: returns license status
Heartbeat-Zyklus
Optionale Online-Bestätigung mit automatischem Backoff bei Rate-Limits.
sequenceDiagram
autonumber
participant SDK as xcess SDK
participant App as Customer App
participant X as xcess.ch API
App->>SDK: scheduleHeartbeats with interval
loop every interval, default 5 min
SDK->>X: POST /api/heartbeat with licenseId and fingerprint
alt success
X-->>SDK: 200 ok with license status
else rate-limited
X-->>SDK: 429 with Retry-After
SDK->>SDK: backoff + retry
else license revoked or expired
X-->>SDK: 200 with status REVOKED
SDK-->>App: emit license.revoked
end
end
Webhook-Zustellung
HMAC-signierte Events mit exponentiellem Retry und Auto-Disable bei Dauerfehlern.
sequenceDiagram
autonumber
participant X as xcess.ch API
participant Q as Delivery Queue
participant W as Vendor Webhook
X->>Q: enqueue WebhookDelivery, eg LICENSE_CREATED
Q->>W: POST with X-Xcess-Signature header t,v1 hmac-sha256
alt 2xx response
W-->>Q: 200 OK
Q-->>X: status SUCCEEDED
else 4xx, 5xx or timeout
W-->>Q: error
Q->>Q: schedule retry 30s, 5m, 30m, 2h, 12h
loop up to 6 attempts
Q->>W: retry POST
end
Q-->>X: status FAILED, auto-disable after 10 consecutive fails
end
Schlüssel-Rotation
90 Tage Karenzzeit: Alter und neuer Schlüssel sind im JWKS, bis Customer-Caches abgelaufen sind.
sequenceDiagram
autonumber
actor R as Reseller Admin
participant X as xcess.ch API
participant DB as Postgres
actor C as Customer SDK
Note over R,X: New key joins JWKS immediately, old key stays active for grace period
R->>X: POST /api/reseller/signing-keys with algorithm
X->>DB: Insert new SigningKey isActive=true
X->>DB: Mark old SigningKey isActive=false, retiresAt = now + 90 days
X-->>R: 201 returns kid and publicKeyPem
Note over X,C: For 90 days, JWKS returns BOTH keys
C->>X: GET /.well-known/jwks/code
X-->>C: returns keys new and old retired
C->>C: Verify by kid, matches either key
Note over X: After retiresAt, old key drops out of JWKS
