CertifiEd documentation
CertifiEd is a cryptographic licensing platform for on-prem software. A license is a self-contained token signed with Ed25519: your application verifies it locally, with no round-trip to the server. Below is the map of every section plus a condensed architecture tour.
Quickstart
From operator login to an activated license — the whole path in curl.
API reference
Panel and client endpoints, auth, error codes, rate limits.
Webhooks
Events, envelope format, the sha256 signature and the retry schedule.
Client SDK
CertifiEd.Client for .NET: offline verification, heartbeat, feature flags.
Hardware binding
The none / fixed / firstActivation modes, rebinding and seat limits.
License protocol
Token format, offline marker, status computation and the grace period.
The problem
On-prem software shipped to customers needs protection against unlicensed use — without permanent internet access and without handing control over to somebody else's cloud.
How the solution works
CertifiEd issues cryptographically signed licenses (Ed25519). The product configuration — feature flags and limits — lives inside the signed token instead of being served over an API: the client application validates the license locally against the public key and keeps working offline for up to 30 days.
- Fully self-hosted. PostgreSQL, MinIO, observability — all on your own servers. No Cloud KMS, no managed services, no SaaS billers.
- Multitenant from day one. A company tree of arbitrary depth (
ltree): the root is a direct customer, descendants are sub-customers and resellers. A role granted on a node applies to the entire subtree. - Offline is the norm. Heartbeats are desirable but not mandatory; the grace window goes up to 30 days.
- Audit everything. Every mutation lands in
audit_log.
Roles and scopes
| Role | Scope |
|---|---|
| Operator | Global (an Ofarandagon employee) |
| Owner / Admin / Viewer | A company and its entire subtree |
Glossary
| Term | Meaning |
|---|---|
Template (LicenseTemplate) | A product; template versions carry the JSON Schema of the config |
| License | An issued signed instance for a company; key CFED-…, file .ced |
| Activation | Binding a license to a hardware fingerprint |
| Heartbeat | A periodic check-in from the client application |
Architecture
A monolith with clean boundaries — Clean Architecture, dependencies point strictly one way.
SharedKernel <- Domain <- Application <- Infrastructure <- Api <- Client (SDK, token format only)- SharedKernel —
Result<T>,Error, primitives. No dependencies. - Domain — entities, enums, value objects. No dependencies.
- Application — use cases, interfaces (
ISigner, repositories), DTOs. - Infrastructure — EF Core (Npgsql, snake_case,
ltree),EncryptedFileSigner,WebhookDispatchWorker, SMTP. - Api — ASP.NET Core; two contours inside one process.
- Client — the SDK, depending only on the token format.
Two API contours
| Contour | Prefix | Authentication | Rate limit |
|---|---|---|---|
| Panel API | /api/v1/panel/* | certified.auth cookie or a PAT via Authorization: Bearer | 300 req/min per IP |
| Client API | /api/v1/client/* | public; mutations are HMAC-signed | 100 req/min per IP |
Details live in the API reference.
Signing and keys
- One Ed25519 key pair per template (
NSec.Cryptography). - Private keys sit in files encrypted with AES-256-GCM; the master key comes from the
CERTIFIED_MASTER_KEYenvironment variable. - Rotation: the new key becomes
active, the old onerotating, thenretired. On compromise a key is markedcompromisedand licenses are re-issued.
Data, background work, observability
- PostgreSQL 16: UUIDv7,
timestamptz,jsonb,ltree(companies.path) for subtree queries. Heartbeats are a candidate for monthly partitioning. BackgroundServiceworkers (no Hangfire in v1): webhook delivery with retries and theX-CertifiEd-Signatureheader, license expiry, heartbeat cleanup.- Serilog (compact JSON) plus OpenTelemetry (traces/metrics, OTLP optional); health endpoints
/health,/health/live,/health/ready.
Deployment
[Caddy / nginx: TLS] |-- panel.certified.example -> panel static build |-- portal.certified.example -> customer portal |-- certified.example -> landing static build `-- api.certified.example -> CertifiEd.Api (Kestrel)
[PostgreSQL 16] [MinIO: backups/export] [Redis: optional] [SMTP]Cloudflare is edge only (TLS / WAF / DNS) — no Workers, no KV. All state stays inside your perimeter.
Environment variables
| Variable | Purpose |
|---|---|
CERTIFIED_MASTER_KEY | base64, 32 bytes — encrypts the private signing keys |
CERTIFIED_KEYS_DIR | Directory of encrypted keys — must be part of your backups |
CERTIFIED_CORS_ORIGINS | Origins of the panel and the customer portal |
ConnectionStrings__Default | PostgreSQL connection string |
Production checklist
- Back up PostgreSQL (
pg_dump+ WAL) andCERTIFIED_KEYS_DIR. - Change the seeded operator password.
- Rate limiting enabled: client 100/min, panel 300/min.
- OTLP pointed at your own Grafana stack, alerting configured.
- Partition heartbeats once the volume grows.
Where to go next
- Quickstart — issue your first license in ten minutes.
- License protocol — what exactly sits inside the token.
- Client SDK — embed verification into your own application.