Self-hosted software: editions, licensing and installs
How one codebase ships both as a hosted service and as software a company runs itself: SaaS, self-hosted and bring-your-own-database compared, the edition decided at build time, offline licences as ES256-signed JWTs with activation by file exchange, Docker Compose and Helm packaging, air-gapped installs with signed images and SBOMs, key management and backups, and why nothing calls home.
In short
Most software today is SaaS: the vendor runs it and customers open a website. Some companies - banks, public bodies, defence suppliers - must keep every byte on their own servers, so they need self-hosted software they install and run themselves. {{brand}} ships both from one codebase: the hosted service, and a Private edition a company installs with Docker Compose or a Helm chart. Instead of payments, a Private installation reads a signed licence it checks offline, and it never calls home.
After this lesson you can Compare SaaS, self-hosted and bring-your-own-database delivery and say when each fits; design a licence that is a signed JWT checked offline, with activation by file exchange; package an application for one host and for Kubernetes, and ship it into a network with no internet using signed images and an SBOM; and plan the keys and backups a self-hosted product needs.
Words you need
- SaaS Software as a service: the vendor runs the software and its database; customers use it in a browser and pay a subscription.
- Self-hosted (on-premises) The customer runs the software on servers it controls - its own data centre or its own cloud account. The vendor ships images, not access.
- Edition Which product a running copy is - here the hosted service or Private. In {{brand}} it is decided by the build, not by a setting.
- Digital signature A value made with a private key that anyone can check with the matching public key: proof of who made a file and that nobody changed it since.
- ES256 A JWT signature algorithm: ECDSA on the P-256 curve over a SHA-256 hash. Small keys, 64-byte signatures.
- JWT JSON Web Token: three base64url parts (a web-safe text encoding), header.payload.signature. A licence file can be one.
- Helm chart A versioned package of Kubernetes templates plus default values. The helm install command renders it with your values and applies it as a release.
- SBOM Software bill of materials: a list of every package and library inside an image, often in the CycloneDX format.
- Kubernetes A system that runs containers across many machines and restarts them when they fail. Helm is its package manager.
- Docker Compose A file that describes several containers on one host and starts them with a single command.
- Seat One person holding a paying role. Self-hosted licences are usually priced by seats.
- Grace period The days a licence keeps working after it ends, so a late renewal does not cause an outage.
SaaS, self-hosted and bring-your-own-database
Three ways to deliver the same product differ in one question: who runs it, and where the data lives. The answer decides who upgrades, who backs up, who can see the data, how the vendor is paid and how fast a customer can start. The middle way, bring-your-own-database, keeps the software with the vendor but puts one customer's records in a database the customer owns.
Bring-your-own-database splits the system into a control plane and a data plane. The control plane - accounts and sign-in, the organization directory, plans, billing - stays in the vendor's database, because one person can belong to several organizations. The data plane - one organization's own records - is routed to that organization's database for every request made inside it.
- Connect The customer gives the product a connection to an empty database and a least-privilege user. The product checks it before it trusts it.
- Prepare The product lays out every module's tables there and moves what the organization already had. Only then is the location marked ready.
- Route Each request made inside that organization opens their connection. A guard on save refuses to write a row into the wrong database.
- Isolate failures Every release migrates every such database. One that cannot be migrated or does not answer pauses only that organization; everyone else keeps working.
- Many versions in the field. SaaS runs one version; self-hosted customers run several at once, so the vendor needs a support policy and upgrade notes per version.
- No access to the servers. Support works from a diagnostics bundle the customer downloads and reads first: versions, readiness, which settings are set - never their values.
- An open alternative for every cloud service. Blob storage becomes any S3-compatible store, a cloud mail service becomes an SMTP relay, cloud monitoring becomes OpenTelemetry.
- Upgrades must be safe without the vendor. Migrations run as a separate step, fail cleanly, and going back means restoring the backup taken just before.
How a licence works in one picture
A self-hosted product cannot ask a server whether it may run, so the permission travels with the product as a signed file. Five steps cover the whole idea. The sections after the options go deeper into each one.
- Issue The vendor writes down who may run the product, for how long and for how many people.
- Sign The vendor signs that text with a private key that never leaves its vault.
- Ship The licence travels as a file. The customer can read it, email it or print it, but cannot change a character without breaking the signature.
- Verify offline The installation checks the signature with a public key that came inside the product. No network call is needed.
- Bind Optionally the licence is tied to one installation, by exchanging two small files that a person carries across.
Deciding the edition at build time
Most products switch behaviour with runtime configuration: a setting, an environment variable, a feature flag. That is right for choices the operator owns. It is wrong for what the operator must not choose. If a self-hosted copy took its edition from a setting, whoever runs it could change one line, run it as the hosted service and skip the licence entirely. So {{brand}} makes the edition a fact of the build: an image compiled with a licence public key is the Private edition, whatever its settings say. The hosted image is built without one.
- Build step The release pipeline hands the licence public key to the build. A public key is not a secret, so it may appear in build logs.
- Compiled in The build writes it into the compiled program, as a constant or as metadata - the detail is the vendor's business.
- Read at start-up The program reads that one value when it starts. A key present means self-hosted; none means the hosted edition.
- One value for everyone Everything that decides the edition reads this one value, so no part of the program can disagree with another.
- Guard the release Release checks make sure no image ships without a real key.
However the value gets into the build, the rule for the code is the same: read it in one place, and add it to the configuration last, so that no setting, environment variable or command-line value can override it.
Compiled is not tamper-proof Someone who patches the binary can remove any check - no scheme survives a determined owner of the machine. A build-time fact stops the one-line bypass and keeps the honest path the easy one; the licence agreement covers the rest. Do not spend months on hidden checks that make support and stack traces worse for honest customers.
The options in the market
- Licence server The installation calls the vendor for a lease, renewed every day or so.; Instant revocation; Exact seat reporting; Breaks air-gapped sites; The vendor's outage becomes the customer's outage; One more service to run and secure
- Signed licence checked offline A signed file with the term and seats; activation by file exchange.; Works anywhere, even with no network; Nothing for the vendor to host; Tamper-evident: every claim is signed; Cannot be withdrawn before its term ends; Seat use is known only at renewal
- Product key with a checksum A serial number whose digits check each other.; Easy to type in; Anyone who learns the rule writes key generators; Carries no term, plan or seats
- Hardware-locked licence Bound to a machine fingerprint such as a network card or CPU id.; Hard to move to another machine; Breaks with virtual machines, containers and repairs; A steady stream of support tickets
- Commercial licensing service A vendor library and portal, for example Keygen or Cryptlex.; Portal, offline files and analytics ready-made; Recurring fees; A third party in the most sensitive path
- Docker Compose bundle One file describes the containers on one host.; One command to start; Easy to read and adjust; Right for a first install or a small company; One host: no scheduling across machines; High availability is the operator's own work
- Shell scripts around docker run A sequence of commands the customer copies.; No extra tooling to learn; No declared state: every host drifts; Upgrades and rollbacks are by hand
- Virtual-machine image or installer An appliance image or a setup program.; Familiar to traditional IT teams; The vendor now patches an operating system; Hard to upgrade cleanly
- Helm chart Templates and values for any Kubernetes cluster.; Upgrades and rollbacks as numbered releases; Hooks run migrations before new pods start; Impossible values are refused when it renders; Needs a cluster and someone who runs it; Template logic gets hard to read
- Plain manifests or Kustomize YAML files, with overlays per environment.; No templating language; Kustomize is built into kubectl; No release history or hooks; Every customer edits YAML by hand
- Kubernetes operator A controller that installs, upgrades and heals the product.; Can automate backups, fail-over and upgrades; Far more code to write and support; Runs with broad permissions in the cluster
What {{brand}} chose, and why
The companies that need a Private edition are the ones with the strictest rules, and some of them have no internet at all. That one fact decided almost everything below.
- Offline signed licences, because a licence server would break the very customers Private exists for. It would also turn the vendor's outage into theirs. The cost accepted: a licence cannot be withdrawn early, so terms follow the contract and renewals are routine.
- ES256 JWTs, because ECDSA is built into .NET and the product already uses Microsoft's JWT library for sign-in. Keys and tokens stay small enough to paste.
- The edition at build time, because a setting is one deleted line away from a free copy.
- Compose and Helm, both: Compose for one host, Helm for companies already on Kubernetes - the two ways nearly every self-hosted product ships. No operator: more code with more permissions than any customer asked for.
- Signed archives as well as images, because an air-gapped site cannot pull from a registry.
- Read-only on expiry, because a licence problem must never become data loss.
An online lease - a short signed lease renewed daily, with a grace period when the vendor cannot be reached - stays designed for later. It would need a hosted signing service with its own key and someone to run it: a running cost and an attack surface no customer has asked for yet.
When another choice is right Selling only to connected customers, with per-seat billing that must be exact? An online lease is fine. A desktop tool for individuals? A commercial licensing service saves months. One very large customer with its own platform team? A Kubernetes operator may pay for itself.
Digital signatures and ES256, from the inside
A licence must be something the customer can read but not change, and that the installation can check without asking anyone. That is exactly what an asymmetric signature gives. The vendor signs with a private key that never leaves its vault; every installation holds only the public key, which can check a signature but never make one.
- Encode Header and payload are JSON objects, each base64url-encoded and joined with a dot. That string is the signing input.
- Hash SHA-256 turns the signing input into 32 bytes. Change one character anywhere and the hash changes completely.
- Sign ECDSA combines the hash, the private key and a fresh random number into two numbers, r and s, 32 bytes each.
- Attach A JWT writes r and s side by side - 64 bytes, not the DER encoding many other tools produce - and base64url-encodes them as the third part.
- Verify The checker hashes the same input and tests r and s with the public key. The answer is yes only if the input is unchanged and the matching private key made it.
ECDSA needs a good random number Signing two messages with the same random number reveals the private key - the mistake that once exposed a games console's signing key. Use your platform's crypto library, which handles this (or derives the number deterministically, as RFC 6979 describes), and never write ECDSA yourself.
A licence as a signed JWT
A licence file is a JWT whose payload says who may run what, and for how long. Because the signature covers every byte, changing the seat count or the expiry date breaks it, so the file is safe to email, paste into a form or print. The type header marks it as a licence, so a different kind of signed file - such as an activation file - can never be taken for one. The field names below are made up for teaching: your own format will differ, and the ideas are what matter.
- Size and shape Refuse anything too long, or not made of three base64url parts, before parsing further.
- Algorithm first Read alg from the header and require ES256 exactly, before any key is touched. This alone stops unsigned alg none tokens and the HS256 confusion attack.
- Key by id, from the build only Look the kid up among the compiled public keys. Never use a key the header carries (jwk) or points to (jku, x5u).
- Signature, issuer, audience, type Verify the signature, then the issuer, the audience and the type header.
- Claims, strictly Every claim must have the right type, a sane range and be present when required. Reject empty or nonsensical values instead of guessing.
- Dates, separately Do not let the token library reject an expired licence. A genuine, expired licence still says what each workspace had, so it can stay readable.
- A new pair Generate a new key pair with a new key id. The private half goes straight into the vault.
- Trust both Ship a build that trusts both public keys, the old and the new. Installations running it accept either.
- Sign with the new id Once customers run that build, sign every new licence with the new key.
- Retire the old key When no licence signed with the old key is still in its term, a later build stops trusting it.
A licence cannot be recalled If a private key leaks, only a new build that stops trusting its key id helps, and every licence signed with it must be issued again. That is why the key id is in the header and the private key lives in a vault.
What a licence means over time
Verifying says a licence is genuine. A second, pure function says what it means now. Keeping the two apart makes both easy to test: the verifier needs keys, the rules need only dates. The rules run on every check, so an expiry takes effect on time and nothing has to be scheduled.
- Missing No licence yet. The server still starts and people can sign in, but workspaces stay read-only until one is uploaded.
- Not yet valid Before the start date, with a few minutes of tolerance for clocks.
- Activation pending Genuine but not yet bound to this installation: people work normally until the activation deadline.
- Active Bound, and inside its term.
- Grace After the end date but before the end plus the grace days: still working.
- Expired, or activation required The product becomes read-only. Nothing is deleted, and a new licence brings everything back.
Read-only, never locked out A licence that ends must never destroy data or lock out the person who can fix it. A smaller licence should never remove anyone who already works - it only refuses new paying members.
- A renewal uploaded early waits beside the current licence and takes over on its start day by itself - nobody has to be at the keyboard that morning.
- Which licence is in effect is computed at the moment it is asked, never stored: one that lets people work before one that does not, inside its term before in grace, then the one issued last.
- Verify every stored licence again on every read, behind a short in-memory cache, so editing a stored row can never promote a licence.
Activation by file exchange
An unbound licence could run on any number of installations. Activation binds it to one. Online products do this with a call to a licence server; an air-gapped site cannot make that call, so the same exchange happens with two small files that a person carries across.
- Upload The administrator uploads the licence. It works at once - nobody waits for paperwork to start.
- Request file They download an activation request: the installation's random id, the licence id, the product version and the time. Nothing else - no names, no usage figures.
- The vendor signs The vendor's issuer tool signs an activation file for that installation and licence, and records it in a ledger, so the vendor knows which installation holds which licence.
- Upload it back The installation checks the file's signature, its type, its installation id and its licence id - and the licence is bound.
- Renew without a second round Later licences are issued for the installation id the vendor now knows, so a renewal is bound from the start.
- A random installation id, made the first time anything needs it and stored in the database - not a hardware fingerprint, which changes when a virtual machine moves or a disk is replaced.
- Count a grace period from facts you can trust: prefer a date the vendor signed to a timestamp in a database row that someone with access could edit.
- Count a grace period from dates the vendor signed, so it does not depend on a clock someone can change. Activation records and the licence agreement cover what no offline check can.
Counting seats with no licence server
A seat is one person with a paying role, counted once across all organizations of the installation. With no server to report to, the installation must enforce the number itself. Every path that can add a paying person has to go through the same check. Decide too what a person without a seat may still do, and say it plainly in the product.
Count under one lock Two administrators in two organizations can both take the last seat at the same moment. Take one installation-wide lock before any organization's own lock - always in the same order, so two requests never deadlock - and count inside it. In PostgreSQL an advisory lock (a lock on a number you choose, not on a row) does this across several servers. A wait that runs too long should refuse the change rather than let it through unchecked.
Packaging: one host with Compose, a cluster with Helm
Both packages run the same two images - the API, with its database migrator inside, and the web app on nginx - next to the customer's PostgreSQL. Shipping the migrator in the API's image means the code and the schema it expects can never come from different commits. Every secret reaches the containers as an environment variable from a file or a Kubernetes Secret - never baked into an image or written into a values file.
Refuse impossible values when the chart renders Helm templates can stop an install with a clear message, for example a fail call that says several replicas need S3 storage. A configuration that cannot work should fail before anything is created and name the value to change - not crash an hour later.
- Read the notes The release notes say what changes and which migrations will run.
- Verify Check the signatures and checksums of what was downloaded.
- Back up The database, the key ring and the files, together - and know the restore works.
- Migrate Run the migrator as its own step. Migrations only go forward.
- Start and check Health endpoints answer, sign-in works, an uploaded file opens and a stored secret still decrypts.
- Going back There is no downgrade: going back means restoring the backup and starting the previous version.
Health probes, proxies and the first administrator
Packaging settles three more things every self-hosted service meets: how the orchestrator asks whether it is healthy, who may reach it, and who the first administrator is.
- Liveness Is the process running? It touches no dependency, so a database outage restarts nothing.
- Readiness Should traffic reach this instance? It needs the database to answer.
- The orchestrator reacts A failing liveness probe restarts the container. A failing readiness probe only takes the instance out of rotation until it recovers.
- Trust forwarded headers only from proxies you name. Otherwise anyone can claim any client address and dodge rate limits and logs.
- Make the proxy prove itself. One technique: the proxy adds a secret header, the service refuses requests without it, and the header is removed before anything is logged.
- Never ship a default administrator. Create the first one with a one-off command run on the host, so nobody is an administrator until the operator decides.
Air-gapped installs, signed images and SBOMs
In an air-gapped network nothing can be pulled: no registry, no package mirror, no licence server. Everything arrives as files, so the customer must be able to prove those files are exactly what the vendor built. Three techniques do that together: pinning by digest, signatures with cosign, and a bill of materials.
- Air-gapped A network with no connection to the internet at all. Software arrives on a disk or through a one-way transfer.
- Image digest sha256:... - the hash of an image's content. A tag such as 1.4 can be moved to other content; a digest cannot.
- Pinning Naming every base image and tool by digest or checksum, in a reviewed file, so a release is built from exactly known inputs.
- cosign Sigstore's tool for signing container images and plain files, and verifying them with a public key - offline, when the transparency log is skipped.
- Attestation A signed statement attached to an image, such as its SBOM: proof the list belongs to this exact digest.
- CycloneDX An SBOM format (JSON or XML) listing every component, its version and licence. SPDX is the other common one.
- Trivy An open-source scanner that finds known vulnerabilities in an image or an SBOM, and can write a CycloneDX SBOM.
- Pin the inputs Base images by digest, tools by checksum, recorded in a reviewed file - never taken from the same place the download came from.
- Build once Both images from one commit, tagged with the version, holding compiled code only: no source files, no symbol files, no development settings.
- Scan A high or critical vulnerability that has a fix stops the release. The scanner reads each image from a file, so it does not need access to the build host's Docker daemon.
- Sign by digest Each image is signed by digest, and its CycloneDX SBOM is attached as a signed attestation.
- Checksum and sign A SHA256SUMS file lists every release file, and the list itself is signed.
- Archive The images saved with docker save, the Helm chart, the Compose bundle, the backup scripts and the guides go into one archive with its own signed checksum list.
Trust the key you hold, not the one in the box An archive may carry a copy of the public key and a verify script for convenience. Neither proves anything: whoever altered the archive could replace both. Verify with the key you received separately, when you became a customer, and run your own copy of any script.
- The signature over the checksums proves the list is the vendor's; the checksums prove each file matches the list. Both, in that order.
- A file the list does not name is a reason to stop: an attacker can add files as well as change them.
- The SBOM lets the customer scan an image built months ago against today's vulnerability data, without the vendor.
- Skipping the transparency log only means the signature is checked against your key rather than Sigstore's public log, where private releases are not recorded. The check itself is just as strict.
- Deploy by digest (image@sha256:...) where you can, so what runs is exactly what you verified. A cluster policy controller can enforce the signature for every pod.
Key management and backups for self-hosted software
A self-hosted product stores secrets it must use but never show again: AI provider keys, saved database connections, webhook secrets. {{brand}} encrypts them with ASP.NET Core Data Protection. It keeps a key ring: a set of keys, each valid for a period, with older keys kept to decrypt what they protected. Where the ring lives decides what a backup must contain - and who could read the secrets if a backup leaked.
- Key ring The set of keys that encrypt stored secrets, kept with the data. Lose it and those secrets cannot be read again, by anyone.
- Authenticated encryption Encryption that also detects tampering: a changed ciphertext is refused instead of decrypting to garbage.
- Envelope encryption Encrypting the keys themselves with another key, such as a certificate, so a stolen database holds only locked keys.
- Create On first use the library creates a key and stores it as XML in the ring's repository: a folder, a cloud blob or a table in the database.
- Protect Each value is encrypted with authenticated encryption under the current key; the key's id travels inside the ciphertext.
- Roll A new key is made automatically before the old one expires - every 90 days by default - and new values use it.
- Keep Old keys are never deleted: every value they protected still needs them to be read.
- Wrap Optionally each key is itself encrypted with an X.509 certificate before it is stored. This is envelope encryption: the database holds locked keys, and the certificate the operator keeps unlocks them.
- Keep the unlocking key apart from what it unlocks: the certificate and its password live in a vault, never beside the backups.
- Encrypt backups by default, for example with age to a public key, and refuse to run without a recipient unless the destination is already encrypted.
- Rotate a certificate by adding, never by swapping: list the old one as a previous certificate so the keys it wrapped still open, while new keys use the new one.
- Record the backup manifest's hash somewhere the backup host cannot write, so a restore can prove nobody altered the backup.
- Drill the restore: restore into a scratch database regularly. A scripted drill proves the database and the key ring come back; also open one stored secret by hand, such as a saved connection, to prove it still decrypts. A backup never restored is a hope.
- Fail at start-up, not at first use: a missing or wrong certificate should stop the server with a clear message, not break the first decryption hours later.
Never just swap the certificate file The server would start happily - and every secret stored under the old keys would be unreadable. Name the new certificate as current and keep the old one listed as previous.
No telemetry home
Customers choose self-hosting so that nothing leaves. A product that quietly sends usage statistics, crash dumps or an update check breaks that promise - and in an air-gapped network every such call is a timeout waiting to happen. So a Private installation makes no call to the vendor by default: no licence check, no heartbeat, no usage report, no update check.
- Its own PostgreSQL, and Redis when it runs several servers.
- An S3-compatible store (AWS S3, MinIO, Ceph) or a local volume for uploaded files.
- An SMTP relay for email.
- An OpenID Connect identity provider: Entra ID, Okta, Keycloak, Auth0, Google Workspace or ADFS.
- An AI provider, or a model server inside the network through an OpenAI-compatible endpoint - or no AI at all.
- An OpenTelemetry collector for logs, traces and metrics - the operator's own.
- Mark it One technique: give every component that would send anything to the vendor a marker interface, so it can be found.
- One door Such a component can be registered only through one helper, which does nothing on a self-hosted build.
- Check at registration Code that registers a sender looks at the edition first. The payment provider is left out entirely on a self-hosted build.
- Test the whole host A test builds the entire application as Private and asserts that no marked service and no telemetry sink exist when nothing is configured.
- Test the addresses Another test reads every shipped setting and every configured HTTP client and fails if one names a vendor address.
The one opt-in exception Error reports are off by default. An operator may keep them locally, or also send them. When sending is on, each report is shown to administrators for a short time first and any of them can stop it. It holds only the version, the error type, the route template, a correlation id and the product's own code frames - never a message, a value, a name or a file.
When the operator connects their own collector, telemetry stays clean too: requests are recorded by their route template (/cards/{id}), never by raw path or query string; outgoing calls by scheme and host only; and people by id, never by name or address. A token that sits in a URL never reaches anyone's logs.
Try it yourself: issue and verify your own licence
In twenty minutes, with Node.js 20 or later and no extra packages, you can build the heart of an offline licence system: a key pair, a signed licence, and a verifier that refuses forgeries.
- Make a key pair Generate a P-256 key pair. In real life the private key lives in a vault and only the public key goes into the product.
- Issue Write a payload with holder, max_users and not_after, sign header.payload with ES256, and print the licence.
- Verify Split the token, require alg ES256 and a known kid, and check the signature with the public key.
- Tamper Change max_users in the payload, encode it again and keep the old signature: verification must fail.
- Forge Make a token with alg none, then one signed with a brand-new key: both must be refused.
- Judge the dates A separate status function maps not_after and the grace days to active, grace or expired. Test it with fixed dates, not the clock.
- Compare with the expected output You should see the payload, then refused: signature, refused: algorithm, refused: signature, then active, grace and expired.
Common mistakes
- Deciding the edition from a setting: one deleted line turns a licensed copy into a free one. Decide it from what was compiled in.
- Letting the token header choose: accepting alg none, HS256 or a key from jwk or jku. Fix the algorithm and take keys only from the build.
- Letting the JWT library reject expired licences: you lose what each workspace had and lock people out. Verify the signature there; judge dates in your own rules and fall back to read-only.
- A hardware fingerprint as the installation id: it changes when a virtual machine moves or a disk is replaced. Use a random id stored with the data.
- Backing up the database without the key ring: the restore succeeds and every stored secret is gone. Back up the database, the keys and the files together, and drill the restore.
- Phoning home just for metrics: in an air-gapped network it is a timeout, and everywhere else it breaks the promise. Make vendor-bound calls impossible on the self-hosted build, and test it.
Interview questions
- How would you license software that runs on the customer's servers? Issue the licence as a signed JWT - ES256, with a private key that never leaves the vendor - and compile the public key into the product, which checks it offline. Bind it to one installation by an activation-file exchange, and make expiry read-only rather than destructive.
- Why ES256 and not HS256 for a licence? HS256 uses one shared secret, so every installation that can check a licence could also sign one. ES256 is asymmetric: installations hold only the public key, which cannot sign, and its keys and signatures are small.
- What is the algorithm-confusion attack, and how do you stop it? A verifier that trusts the alg header can be handed an HS256 token whose HMAC secret is the public key, or an alg none token with no signature. Require the one algorithm before looking at any key, and take keys only from your own trusted set, never from the header.
- Why decide the edition at build time? Configuration belongs to whoever runs the software. If a setting chose the edition, deleting it would turn a licensed copy into the hosted edition with every plan free. A key compiled into an assembly can only be removed by rebuilding.
- How does activation work without a network? The installation exports a request with its random id and the licence id. The vendor signs an activation file for that pair and records it in a ledger. The administrator uploads it, and the product checks its signature, type and both ids. Meanwhile the licence works, until a deadline the vendor signed.
- Compose or Helm for a customer install, and why? Compose for one host and small teams: one file, one command. Helm for clusters: versioned releases, hooks that run migrations before new pods start, and rollbacks. Both ship the same images.
- How do you ship to an air-gapped network? Ship a signed archive: images saved with docker save, the charts and scripts, and a checksum list with a signature over it. The customer verifies it with a public key received separately. Inside, the product must need no outbound connection at all.
- Why pin images by digest and not by tag, and what does an SBOM add? A tag can be moved to different content; a digest is a hash of the content, so what you verified is exactly what runs. An SBOM lists every component in an image, so the customer can scan it against today's vulnerability data without the vendor.
- What must a self-hosted backup contain? The database, the key ring that decrypts its stored secrets, and the uploaded files, taken together. The key-ring certificate and the backup key stay apart in a vault. Drill the restore, and open one stored secret by hand to prove it still decrypts.
- How do you rotate the key that signs licences? Make a new pair with a new key id and ship a build that trusts both public keys. Sign new licences with the new key once customers run that build. Remove the old key when no licence signed with it is still in its term.
Related
- /help/technology/payments
- /help/technology/secure-links-privacy
- /help/technology/azure-devops