Licensing, credits and issuer numbers

Read this if you are choosing a plan, expect to mark more than the free allowance, or need to know what a licence changes on a running deployment. You will know what is free, how credits are counted, what a mark carries in each state, and how licences and issuer certificates are installed and renewed. Skip if you only detect or verify (both are free and need no licence), or you want prices (laidline.com/licensing). Time: 10 minutes for the first half; the rest is reference. Needs: pip install laidline to run the commands.

In short

  • Detection and verification are free in every state and never read a licence.
  • Marking is counted in credits per calendar month (UTC); the first 1,200 credits a month are free (1,200 images, or 100 minutes of video, or 400 minutes of audio).
  • Over the allowance without a licence, marking never stops: new marks are shared marks and the compliance paperwork in the evidence export is withheld.
  • Registering is free and gives you a registered issuer number and a private payload key, so your marks name you; paid plans raise the monthly allowance.
  • Licences and issuer certificates are signed files, verified offline under two pinned Laidline roots, and renew on their own.

This page describes what the software does. The terms are in the Laidline Customer Licence Agreement, which this page does not summarise.

What is free, what is metered, what is locked

  • Free in every state: detection and verification. /detect, laidline detect, laidline evidence verify and the bundle's verify.py never read the licence, the meter or a certificate, and grades never depend on them, including after a licence lapses.
  • Metered: marking, in credits per calendar month (UTC). The first 1,200 credits each month are free: one allowance per company, registered or not, however many deployments it runs. The software counts each evidence log separately and the customer agreement sets the company-wide figure (How credits are counted).
  • Never stopped: marking and evidence recording. Over the allowance, without a licence, after a licence lapses or with a broken licence file, every mark is watermarked, signed, logged, checkpointed and timestamped exactly as before, and the mark and proxy responses do not change.
  • What changes over the allowance: the payload key new marks carry (What a mark carries) and, without a licence, the compliance paperwork in the evidence export (What happens over the allowance).
  • Never locked: the export itself, and the raw evidence log, checkpoints and certificates on your disk. Nothing deletes, encrypts or locks them, and verify.py keeps working on every bundle you have exported.

Plans

The plan id is what laidline register --plan takes and what a licence's grant.plan carries. Prices are on laidline.com/licensing; none appear in the package.

Plan --plan Credits a month Signing keys Billing
Free registered_free 1,200 1 none
Lite lite 15,000 1 monthly or yearly
Starter starter 150,000 2 monthly or yearly
Growth growth 1,000,000 5 monthly or yearly
Scale scale 10,000,000 10 monthly or yearly
Enterprise sold by contract unlimited unlimited by contract
  • Free: registering costs nothing. The free licence runs 12 months and renews itself.
  • Paid plans: you buy and change them in the customer portal at https://account.laidline.com (sign in with a work email at your registered domain). laidline register bills monthly unless you add --interval year. A monthly subscription gets a licence for each paid month, a yearly one a licence for the year; each paid invoice issues the next licence (Automatic renewal). Moving from yearly to monthly billing takes effect at the end of the paid year.
  • Grace: every licence covers the period you pay for plus 30 days.
  • Over a paid grant: credits above the plan's monthly figure are a True-Up under the customer agreement, not a switch: your marks stay private and nothing is withheld, and the credits above the plan are charged at the plan's overage rate (True-Up).
  • Scale and Enterprise may carry their own weight table; it prices every month that licence unlocks. Enterprise adds an organisation CA binding and tenant reports.
  • Earlier plan ids stay valid and read as the plans that replaced them: registered as Starter, unlimited and platform as Enterprise.

laidline usage names the smallest plan whose monthly figure covers a month's credits (suggested_band), and laidline licence status prints the same as suggested band:.

True-Up

Credits above the plan's monthly figure are charged after the Month, at a fixed rate for each credit above the plan. The rate is the plan's price per credit plus 25 percent, the same on monthly and yearly billing:

Plan Overage rate, per credit above the plan
Lite $0.0124
Starter $0.0042
Growth $0.0025
Scale $0.000625
Enterprise the rate its Order states
  • Which plan decides: the highest plan you paid for during the Month sets both the credits that count as the plan and the overage rate. Each Month stands alone; credits are not pooled across a year.

  • Which Months are billed: the Month a paid plan starts is not billed, and neither is the Month of Lapse.

  • When it is settled: on the 16th of the next month, the day after the usage-report deadline. Laidline emails the amount that day.

  • How it is charged: as a separate invoice, charged to the card on file on the 23rd, or 7 days after the email if that is later. Monthly and yearly billing alike. Until then you can dispute it at billing@laidline.com.

  • Under 50 cents: the amount is waived. It is not charged, not carried to a later Month, and you get no email.

  • The amount emailed is the amount charged, or nothing is, apart from any tax. If your account with the payment processor holds a credit or an unpaid balance on the charge date, nothing is charged automatically: the charge is held for review by hand and your balance stays as it was.

  • A charge held for a credit: if Laidline approves it, your card is charged in full, as a card payment with a receipt and no invoice, for the amount emailed plus any tax. Your credit stays untouched.

  • A failed charge never touches your plan, your licence or its renewal.

  • Counts come from your reports (and, if you turned reports off, from a signed Usage Statement). Laidline holds no logs, so a report is your own declaration, and the agreement's audit clauses stand behind it.

  • Your marks stay private throughout, and nothing is withheld.

How credits are counted

The evidence log is the meter: every mark is already an evidence entry, and credits are computed from those entries when they are read. There is no separate counter.

The signed weight table that ships in the package, version 1.0.1, prices marks from November 2026:

Medium Credits What 1,200 credits buy
Image 1 per mark, at any size 1,200 images
Video 0.2 per second 100 minutes
Audio 0.05 per second 400 minutes
Text 0.05 per 1,000 tokens 24 million tokens
Unmarked or failed output 0
  • Example: a month with 900 images and 30 minutes of audio costs 900 + 1,800 s x 0.05 = 990 credits, inside the free allowance.

  • Voice calls: a streamed call is metered like any other audio, 0.05 credits for each second of audio your bot sends through the marker, which is 3 credits a minute. Only that audio counts: the caller's voice, hold music and the gaps between turns never pass through the marker, so they cost nothing. Silence inside the bot's own audio counts, and audio below 16 kHz is not marked and counts 0. A 5-minute call with the bot speaking throughout is 15 credits; an hour of bot speech is 180. The free allowance of 1,200 credits is 400 minutes, about 80 such calls. At each plan's monthly price (plan price divided by plan credits; yearly billing is cheaper, since a year costs 10 months):

    Plan Per minute (3 credits) 5-minute call (15 credits) Per hour (180 credits)
    Lite $0.03 $0.15 $1.79
    Starter $0.01 $0.05 $0.60
    Growth $0.006 $0.03 $0.36
    Scale $0.0015 under $0.01 $0.09
  • Content Credentials without a watermark: an image signed but not watermarked counts like a marked image.

  • Rounding: the month's total is rounded once, half up to 0.01. A single entry's rounded figure is illustrative, and per-medium subtotals are each rounded, so they need not add up to the total exactly.

  • Start-up self-test: up to 10 self-test marks per UTC day per deployment are not metered. Beyond that a self-test mark is an ordinary, metered mark and its result warns self-test cap reached (10 per day); this mark is metered.

  • Per evidence log: the allowance applies per evidence log (per authoritative sink). Deployments with separate logs each count separately; the customer agreement, not the software, sets an entity-wide figure.

Check this month's credits

laidline licence status

Expected output on a new, unregistered deployment:

state: free_within_cap
plan: none
allowance: none
credits this month: image 0, video 0, audio 0 (total 0)
suggested band: registered_free
marks this month: private 0, shared 0
self-test marks: 0
issuer: unregistered shared number <your trial number>
directory: snapshot 2026-10-21 serial 10
licence: absent

What just happened: Laidline read this month's entries from your authoritative evidence sink, priced them with the weight table in force and printed the state. allowance: shows a licence's grant (none before you register); the free 1,200 credits apply either way. laidline licence status --json prints the same as one JSON object.

The formula and the table in force

Credits are computed from facts every entry already records (modality, the event (marked, unmarked, failed), watermark scheme, Content Credentials presence, audio duration_s, video frames/fps, text completion_tokens) and from the entry's meter block (pixels, env, selftest; see Meter blocks). Per entry:

(per_mark + per_second x seconds + per_megapixel x pixels / 1e6 + per_1k_tokens x tokens / 1000)
  x outcome_factor[marked|unmarked|failed] x signed_only_factor (C2PA without a watermark) x env_factor[env]

with the row <medium>/<scheme> if the table has one, else <medium>/*, else meter_missing_as. The table names the monthly allowance as free_cap_units_per_month and its rounding as rounding.

Table in force: a month is priced by the newest table this installation knows whose effective_from is at or before that month; a month before the earliest table's effective_from is priced by the earliest table. A later table never re-grades an earlier month. Every status, notice and statement names the table version it applied.

A table carried in a licence. A licence may carry its own weight table, either inline (grant.weights) or by naming a shipped table (grant.weights_version). That table prices every month the licence unlocks: its own effective_from is not consulted, so a licence that starts in the middle of a year prices all of its months with its own table, and the months an earlier licence unlocks keep that licence's table. Where two licences unlock the same month, the one that ends last (a renewal) decides, so each month is priced by exactly one table. Marking, the usage statement, the usage report, laidline licence status and the evidence export all apply this one rule. An inline table has the same fields as a published table, including free_cap_units_per_month; Laidline copies that figure from the published table, and inside a paid licence it decides nothing, because the licence's own grant is the limit. A licence table that cannot be used never stops marking or the export: the shipped table prices the month, and the problem is named (Licence states).

The start-up self-test is excluded by an in-process flag only (it counts for no credits and no switch); a request header such as x-laidline-route: startup:selftest never excludes a mark. Self-test marks are still shown, as self-test marks: N per month in laidline licence status, the export's coverage line and verify.py (selftest_marks in the status JSON, the usage statement and the monthly usage report).

What happens over the allowance

The first mark that takes a month over 1,200 credits changes two things, and only for that UTC month:

Your deployment New marks for the rest of the month Compliance paperwork in the export
Unregistered, no licence shared marks (as before: unregistered marks are always shared) withheld for the month
Registered on the free plan shared marks under your trial number, instead of private marks included; the month reads over allowance, not attested
Paid plan, licence current or in grace private marks, unchanged (over the grant is a True-Up, charged at the plan's overage rate) included
Licence lapsed past grace, invalid, or certificate revoked shared marks withheld for months over 1,200 credits that no licence unlocks

When the paperwork is withheld, the export still runs and the bundle is complete and signed: summary.md opens with an UNLICENSED USE notice (months, credits, allowance, table version); the filled-in compliance texts, the licence clause and the packaged robustness and phantom-decode figures read "not included: licence required"; and manifest.json carries a licence block. The raw log, the checkpoints, detection and verify.py are untouched.

The first mark of the next UTC month sees zero credits again: a registered deployment's marks are private again, and nothing marks the return. If you receive a lawful order for records whose paperwork is withheld, Laidline issues a licence for the demanded months free of charge (Lawful orders).

What a mark carries

The payload key and the issuer number of a new mark depend on the registration. The choice is made once per mark, at mark time, from local files and the evidence log: nothing is checked online, and an error in the choice falls back to the private key if the certificate binds one, else to the shared key. The two key families are described in Payload keys: The shared family and the private key.

State Key in the new mark Issuer number What a verifier sees
unregistered the shared pool key the trial number laidline init drew "shared mark", grade D: a Laidline mark is confirmed, the provider is not identified
registered free, within the month's allowance (the grant's units_per_month; 1,200 credits on the free plan) the private key bound in the issuer certificate the registered number "registered to {name}" per the directory snapshot, with the URL to verify at; grade B on confirmation
registered free, allowance crossed the shared pool key, for the rest of the UTC month the trial number (the certificate's prior_trial_ids[0]) as unregistered
paid, licence current or in grace the private key, never a fallback the registered number as registered; credits above the grant are a True-Up at the plan's overage rate, not a switch
licence lapsed past grace, licence invalid, or certificate revoked the shared pool key the trial number as unregistered

The private key is used only while an installed issuer certificate that binds this deployment lists its key_fp for the medium. An expired, not yet valid, revoked or unbound certificate counts as none. A grant with no plan counts as paid. Credits are counted from the evidence log, self-test entries excluded. A long-running process re-reads the certificate and licences hourly; a read that fails (a file that is there but cannot be read) keeps the last good inputs until the next read, while a removed certificate or licence takes effect.

The switch happens at mark time only. The directory entry, the certificate and the licence are not touched mid-month. The first mark that would cross the allowance is made shared and appends one payload_key_switch evidence event, {month, switched_to: shared, at_credits, key_id, issuer_id}, before its marked entry; later marks that month read the event and stay shared. The first mark of the next UTC month sees zero credits and is private again; nothing marks the return. A mark made while private keeps its registered number and private tag and resolves to "registered to {name}" for as long as the directory lists the number, including after the licence lapses. Lapsed and revoked read differently:

  • lapsed (licence or registration ended): the verifier is told the number was registered to {name} until {date} per a snapshot, that a watermark does not say when it was made, and that the provider's evidence record (RFC 3161 timestamps) can show the date. Attribution stays.
  • revoked (key compromise or terms, never non-payment): the verifier is told the registration was revoked on {date}. Attribution is stripped, and new marks are shared.

Coverage and features in an export

Lock mode. The weight table's or the grant's lock_mode says what is locked over the allowance; the only mode in effect is withhold_paperwork (a value of refuse_export reads as withhold_paperwork; nothing refuses a plain export for the allowance). The export never locks by accident: with no valid weight table, or if the licence code itself fails, nothing is withheld (the manifest then carries a licence_check_failed:<type> warning).

Features enforced. Only export, paperwork and tenant_reports on a licence mean anything; any other feature string is ignored, and marking, detection and decoding are never gated. export belongs to the licence attestation in a bundle, paperwork to the compliance paperwork and tenant_reports to the platform filters (a tenant filter on an export or a statement). A licence without export still unlocks its months, but the bundle reports them as licensed, not attested. A licence without paperwork withholds the paperwork (the Measure texts, the licence clause and the packaged figures read "not included") for the months it unlocks that are over the free allowance, and summary.md says "Paperwork: not included: the licence for <months> does not include the paperwork feature"; it never refuses the export. The one refusal is LicenceRequired: a tenant filter without tenant_reports (on every decided month). It is CLI exit code 3 (laidline evidence export --tenant, laidline usage --tenant) and HTTP 402 {"reason": "licence_required", "detail": ...} on /admin/evidence/export?tenant=, and its text ends with the lawful-order note (Lawful orders).

Coverage in an export. The detection-access text carries a line - Coverage: (when the paperwork is withheld, summary.md carries the same line near its top): licence months <months or none>; issuer <number>: registered (or registration absent, with (trial) for a trial number, or registration unknown when the registration could not be read); the licence months are the months whose status is covered or true-up; then, for each month the export decides, <month>: private P, shared S, self-test marks: N, <status>, where P and S count the month's marked entries by watermark.key_family (shared; anything else or absent counts as private: text marks, signed-only images (watermark_skipped) and, on a deployment without an issuer certificate, marks under a hand-written registered-range issuer_id) and N counts its self-test marks apart (the meter leaves them out). The manifest's licence.months carries the same private, shared and selftest_marks counts, and verify.py recounts all three. The status is one of six words:

Status word Meaning
covered A licence unlocks the month, within its grant, and carries the export feature.
true-up A paid licence unlocks the month and the usage is over its grant; the credits above it are a True-Up at the plan's overage rate.
over allowance, not attested A registered free licence unlocks the month and the usage is over its allowance.
not covered The month is over the free allowance and no licence unlocks it (the paperwork is withheld).
absent No licence unlocks the month and nothing is owed, or no licence decision was made.
licensed, not attested (no export feature) A licence unlocks the month but lacks the export feature. The month is left out of the "licence months" list, as is every month whose status is not covered or true-up.

The same counts and statuses are in the manifest as licence.months, keyed by month ({private, shared, status}, the status as an identifier: covered, true_up, over_allowance, not_covered, absent, not_attested). verify.py recounts every month from the signed streams and fails the bundle on a difference (licence_months_mismatch:<month>); it prints each month's line under its licence: line.

--tenant TENANT (laidline evidence export) or ?tenant=TENANT (/admin/evidence/export) narrows the counts to that tenant's entries. It needs a licence with tenant_reports unlocking every decided month; otherwise the export is refused (exit 3 / 402). The streams, checkpoints and fingerprint records stay whole: only the Counts tables and the per-month counts of the coverage line are narrowed, the manifest names the tenant, and summary.md has a line saying so.

  • Which months decide: an export of a date range is decided by the months of the asset entries inside that range. The bundle also carries the entries after the range up to the checkpoint that anchors it. Those entries decide nothing when they fall in a month after the month of --to and the stream carries at most 1,000 of them (the default evidence.checkpoint.every_n); an entry carried in the month of --to or earlier, or in an extension longer than that, decides like an entry inside the range. A range with no asset entry inside it (for example the instant of a checkpoint event) is decided by every asset entry the bundle carries. When the manifest carries a licence block, its decided_months lists the months that decided.
  • Months a licence unlocks: month m is unlocked when the licence's covers_from is at or before m (or null) and m ends at or before not_after plus renewal.grace_days. Months inside a licence window stay unlocked after the licence lapses. A licence's own grant.lock_mode applies only while the licence is inside its window plus grace.
  • Over the grant of a licence: summary.md carries a line naming the months, credits and grant (true-up due) and the manifest's licence.over_grant lists them. The amount is settled as in True-Up. The grant withholds nothing by itself (a licence without paperwork still withholds the paperwork of its over-allowance months).

Lawful orders

If you receive a legal summons or another lawful order for your records and the paperwork it needs is withheld, Laidline issues you a Legal-Process Licence, free of charge and promptly once the items below arrive (clause 12 of the customer agreement). Laidline never holds your logs: they stay on your servers, and your own deployment produces the export. The licence-required refusal (a tenant filter without tenant_reports; withheld paperwork is not a refusal) ends with a note pointing here.

A Legal-Process Licence is an ordinary licence file covering the period demanded:

  • features export and paperwork, plus tenant_reports where the order concerns one tenant;
  • covers_from set to the first month the order demands;
  • a validity window on month boundaries equal to the demanded months: not_before the first instant of the first demanded month, not_after the first instant after the last demanded month, renewal.grace_days 0, so the licence unlocks exactly the demanded months and nothing after them (a licence with a short validity from its issue date and covers_from at the first demanded month would unlock every month up to the issue date);
  • bound to your signing key like any licence.

Such a licence is already past its window when it arrives, so on status surfaces it shows licence_expired:lapsed if it is your only licence; the export of the demanded months is unaffected. An export that carries only this licence holds it as licence/licence.json; a bundle that carries several licences holds each as licence/<licence_id>.json. Where a term licence also unlocks a demanded month, the month is attributed to the licence with the latest not_after.

You install it (laidline licence install) and your deployment then exports the full signed bundle, with the Measure texts and Laidline's licence attestation, for exactly those months. It is the same laidline.licence document, verified the same way.

The process is described in Laidline's legal-process policy at https://laidline.com/legal/legal-process and in clause 12 of the customer agreement:

  • What to send to legal@laidline.com: a copy of the order, the name of the issuing court or authority, its file reference and a contact there. A deployment that is not registered also sends the public certificate chain of its signing key with the order, so the licence can be bound to that key.
  • Timing: Laidline issues promptly once those items arrive. It may check the order with the issuing court or authority, without delay, and may wait for that reply before issuing.
  • Refusal: Laidline refuses a Legal-Process Licence only for a failed check.
  • Limits: a Legal-Process Licence unlocks export and paperwork for the demanded months, and tenant_reports where the order concerns one tenant. It licenses no marking, and it never satisfies the software licence's condition for marking above the allowance (that needs a Laidline licence other than a Legal-Process Licence). It creates no fee and settles no fees or claims for the demanded months.

Licence states

State When
free_within_cap no valid bound licence; this month's credits at or under the allowance (or no valid weight table)
over_cap_unlicensed no valid bound licence; this month's credits over the allowance
licensed a valid licence bound to this deployment covers now; credits within its grant (or unlimited)
licensed_over_grant as licensed, credits above the grant (a True-Up, not a lock)
licence_expired:grace the newest licence's not_after has passed by less than its grace days
licence_expired:lapsed past the grace window
licence_invalid:<reason> licence files are present but none is valid and bound: bad_signature, unknown_signer, revoked, not_bound, malformed, not_yet_valid, signer_expired, signer_not_yet_valid, signer_not_permitted

Flags and warnings:

  • meter_unknown: the in-process tally has not finished rebuilding, or the evidence sink cannot be read. It appears on operator surfaces only and never decides a lock.
  • weights_invalid: no shipped weight table verified, so the credits are unknown, no cap applies and nothing is withheld (laidline licence status prints credits: unknown (no weight table); the usage report lists the warning). Reinstall Laidline if you see it. A healthy installation never shows it.
  • weights_invalid:licence (the licence's inline table is malformed) and weights_unknown:licence (the licence names a table version this installation does not ship): a table carried in a licence could not be used, so the shipped table priced the month instead. They appear in laidline licence status (the flags: line and the JSON flags list), as warnings in the usage statement, the usage report and the export manifest, and laidline serve logs one warning per process for each.
  • clock_in_doubt and issuer_state:<state>.

Changes of licence state are not recorded as events in the evidence chain.

Clock. Licence validity is judged against the later of the system clock and the latest TSA time verified against the TSA roots (signing.tsa.roots_path). The default, builtin, is the public DigiCert and Sectigo roots that ship inside Laidline, so tokens from the default TSAs are verified when they arrive on every deployment, with no setup. If you use another TSA, set signing.tsa.roots_path to its published roots; the literal builtin covers only timestamp.digicert.com and timestamp.sectigo.com, so with any other host configured it loads no roots. The usage statement says clock: customer_only (no TSA time is used for licence purposes) when roots_path is null or empty, when no TSA URL is configured, when a checkpoint covering the month has no verified token (for instance a TSA outage while it was written), or when roots_path is builtin with a TSA host other than those two. A finished month still closes in all of those cases: only a covering checkpoint whose token was checked against the roots in force and failed (verified_against_roots: false) blocks closing, because absence of a token is not failure. A failed verdict is permanent for that month in the statement (the covering checkpoint is the first one past the month's end and never changes); the export still verifies the token with --tsa-roots pointing at the roots it chains to. Each TSA URL is tried for a token that verifies; when none does, the first well-formed token is still stored, marked verified_against_roots: false, so a roots file never costs a checkpoint its timestamp. Only verified tokens feed licence decisions.

Late anchoring. The usage statement flags late_anchoring:<stream> for each evidence stream whose covering checkpoint carries a timestamp that was verified against your TSA roots and is dated more than one day after the end of the month. Verified times exist by default (the built-in roots, for the default TSAs); the flag is never set in the cases listed under Clock, for instance signing.tsa.roots_path null or empty, or a timestamp authority that is not reachable.

Licence files

A licence is a JSON document signed by Laidline (laidline.licence, Ed25519 over the JCS canonical form). It names the licensee, its validity (not_before, not_after, renewal.grace_days), covers_from, the issuer certificate serial, the keys it binds (bound_keys.signing_spki_sha256: the SHA-256 of your signing key's public key, the value checkpoints record as signer.public_key_sha256; or org_ca_sha256: your organisation CA certificate), the grant (units_per_month or unlimited, optionally its own weight table or lock mode) and the features it includes.

Where Laidline looks:

  • licence.path (default licence.d beside the config file): one file or a directory of *.json files; or
  • LAIDLINE_LICENCE: the licence JSON, base64 on one line (it replaces the path).

Several files may coexist: the valid file bound to this deployment with the latest not_after decides the state; every other file is reported as a warning with its reason and never decides the state, so a bad file dropped beside a good one cannot lock you out. Each file and the variable are limited to 1 MiB; anything larger or not parseable is reported as malformed.

Binding is proven by evidence, never by a configuration file alone (certificate chains are public), and a licence unlocks evidence entries, not whole months of whatever a bundle holds. An entry is licensed when a licence unlocks its month and binds either the active signing key that signs the manifest (once the signing backend has shown it holds that key), or the key that signed a checkpoint whose range holds the entry and whose head matches the log. A month is unlocked only when every deciding entry in it is licensed. So a key rotation does not relock months a licence paid for (the retired key's checkpoints cover them), while another deployment's stream copied into a sink licenses only that stream's entries. An organisation CA counts only on the verified certificate path upward from such a key. Laidline reissues a licence bound to a new key.

Install and renew: laidline register builds the request (your licensee name, signing key fingerprint and chain, your current issuer number, the fingerprints of your active payload key and your acceptance of the customer agreement; signed with your signing key; no secrets and no usage unless you add --usage), sends it to the issuing service and installs the licence and the issuer certificate it returns.

The request document (laidline.request) carries the acceptance as a terms block, {"version", "url", "accepted_at"}: the agreement's version and URL as the package ships them (laidline.licence.terms) and the moment you accepted, in UTC to the second (2026-10-18T09:30:15Z). The signature covers the block like every other field, so the acceptance is part of the durable signed record the issuing service keeps, not only of the POST body. The issuing service refuses a request without the block (terms_required) or with a version it does not accept (terms_unknown). Renewal is a new file; keep the old one as proof of the months it covered. To install a file you received by another channel, use laidline licence install and laidline issuer install, in either order, then reload. The commands are in Commands.

The documents proof. The issuing service returns the licence and issuer certificate of an issued registration only to the deployment whose signing key the registration binds. A request for them carries a Laidline-Proof header: a short statement (the registration, the issuing service's address, the time and a random nonce) signed with the deployment's active signing key. Without a proof, or with one from a key the registration does not bind, the answer still gives the registration's status, with "documents": "proof_required" in place of the documents. A proof counts for one request within five minutes of the issuing service's clock, so every fetch signs a new one. The deployment signs each fetch with its registered key; a rotated key must be bound before it can fetch: run laidline register again with the new key active. Nothing secret is stored beside the licence files, and a host that holds the signing key (or access to the same KMS key) can fetch.

Automatic renewal

A deployment fetches its renewed licence on its own. When the newest installed licence bound to it is within 7 days of its not_after, or past it but inside the grace, the server (laidline serve, 30 seconds after start and then daily) and the commands that read the licence (laidline licence status, laidline usage, laidline evidence export) ask the issuing service for the documents of the registration recorded in licence-fetch.json, with a documents proof signed by the active signing key (the documents proof), at most once per UTC day. laidline register and laidline licence fetch record that registration when they install its documents. A command run without the signing credentials (for example laidline licence status from a shell or cron that cannot reach the KMS key) logs that it cannot sign the proof and does not use up the day's attempt, so the server still fetches that day. The issuing service answers with your newest licence and current issuer certificate; when the answer names a different registration as current (current_registration_id), the file records that one for the next attempt. The file sits beside the licence directory (beside the config file by default) and also holds the time and outcome of the last attempt.

A fetched licence is installed only when it verifies under the pinned roots, binds this deployment's signing keys, has not lapsed and is newer than every installed licence (a later not_after, or the same with a later issued_at); an issuer certificate in the same answer is installed when it maps the same number and ends later than the installed one. The previous licence stays installed as proof of the months it covered. A failed or empty fetch never stops or changes marking: it writes one line to the server log (or to stderr for a command) and is tried again the next day. The server picks up a new licence within the hour, or at once on SIGHUP. licence.auto_fetch: false turns automatic renewal off for an air-gapped host, as do LAIDLINE_OFFLINE=1 and a set LAIDLINE_LICENCE (the variable shadows any installed file); renew such a host with laidline licence install.

More than one host

One organisation registers once. A second host of the same deployment does not register again: a second request from the same email domain is denied at once on the one-key plans (registered_free and lite), with the hint to copy the registered host's identity, and goes to review on plans that bind more keys (Starter 2, Growth 5, Scale 10), where you register with --plan starter, growth or scale from the second host. The second host carries the registered host's identity instead: laidline.yaml (its watermark.issuer_id included), payload-k1.key, the signing key and chain (certs/, or access to the same KMS key), the licence files in licence.d/ and issuer-certificate.json. A host that holds only the licence and the issuer certificate stays licence_invalid:not_bound, because the licence binds the signing key of the host that made the request; a host whose watermark.issuer_id is neither the number the certificate maps nor one of the trial numbers it lists as its own prior numbers (prior_trial_ids; laidline register leaves the file as init wrote it) keeps making shared marks while serve logs certificate_invalid:number_mismatch. Grade B (confirmed_bytes) on a read needs the asset's registry entry, which is in the evidence sink of the host that made the mark: give every host of the deployment the same authoritative evidence sink (s3 or postgres, Sinks); with a per-host file sink a second host reads the deployment's own registered marks as present_unverified grade D with registry: missing.

Licences in an export

A licensed export carries the licence that unlocks each exported month (licence/licence.json for one licence, licence/<licence_id>.json each for several, for example after a renewal) and licence/laidline-roots.json; the issuer certificate travels as licence/issuer-certificate.json once issuer.certificate_path is allowed. The manifest's licence.licences lists which months each licence unlocks. The bundle's verify.py checks each licence against the Laidline roots pinned in the script itself (not the bundle's copy), counts only keys that verifiably signed the bundle (the manifest signer when manifest.sig verified, checkpoint signers whose signatures verified, an organisation CA on a verified path from one of them; a checkpoint counts only when its head also matches), applies the same entry-level rule, and reports licence: verified (covers <months>) (at least one month in range covered and none uncovered), not_covering_range, invalid (<reason>), absent or unsigned_pack (a licence in a bundle whose manifest is not signed is never reported verified). Months present in the bundle outside the requested range and not covered are named on a second line.

Commands

  • laidline register --email ADDRESS --display-name NAME --accept-terms [--plan registered_free] [--interval month|year] [--country CC] [--detect-url URL] [--disclosure-url URL] [--name LEGAL_NAME] [--usage NOTE] [--wait|--no-wait] [--timeout 30m]: registers this deployment. Registration publishes the display name (it must contain the verified domain's label) and the domain beside the issuer number; --display takes only public, and --display withheld exits 2. A deployment that does not want a public name stays unregistered, on shared marks. --accept-terms accepts the Laidline Customer Licence Agreement; the request carries the signed terms block (version, URL, accepted_at). Without the flag the command asks Accept the Laidline customer licence agreement <version> at <url>? [y/N] on a terminal (y counts as the flag); otherwise, or on any other answer, it prints the version and URL and exits 1 before anything is signed or sent. It needs an active payload key (laidline init writes one; exit 2 otherwise), signs the request with the configured signing key (a KMS, Vault or local key all work) and sends it to issuer.registration_url. When the answer is email_pending it prints the address and polls every 5 seconds until --timeout; on issued it verifies and installs both documents and prints a summary; on review or payment_pending it prints the reason (or the checkout URL), a hint for the common reasons, and the resume command, and stops waiting. A refused registration exits 1. Running it again after laidline payload-keys generate binds the new key. With an active subscription in the customer portal on your domain, laidline register --plan <paid plan> issues on email verification without a checkout; otherwise the command prints the checkout URL, and laidline licence fetch REGISTRATION_ID resumes after payment. --interval sets how a paid plan is billed: month (the default) or year; every request carries it, and with --plan registered_free it is accepted and has no effect.
  • laidline licence fetch REGISTRATION_ID [--timeout 30m]: resumes a registration, polls until the documents are issued (printing each state change once), then verifies and installs them; like register, it stops on review (the reason, the hint for the common reasons and the resume command, exit 0) and on payment_pending (the checkout URL) instead of waiting, and a refused registration prints the hint for the common reasons and exits 1; if --timeout passes first it prints stopped waiting and the resume command and exits 0: check laidline licence status.
  • laidline licence install FILE: verifies a laidline.licence under the pinned roots and copies it into the licence directory (licence.path, default licence.d).
  • laidline licence status [--json]: the licence state, plan (an earlier plan id shows as the plan that replaced it, starter (the licence says registered); in JSON plan is the current id and licence_plan the licence's), allowance, credits used this month per medium, private and shared mark counts, the switch event if there was one, the issuer line (a certificate whose number the config does not use is reported as number_mismatch with the number to set), the flags: line (the JSON flags list; shown only when a flag applies) and the directory snapshot with its staleness caveat. Like usage and evidence export, it first fetches a renewed licence when one is due (Automatic renewal).
  • laidline issuer install FILE: installs an issuer certificate or a directory snapshot (by its type), verified under the pinned roots; a directory's serial must not go backwards.
  • laidline issuer refresh [--url URL]: fetches the directory snapshot from issuer.directory.refresh_url, verifies it and writes it; on any refusal the installed file stays and the command exits 1. Run it by hand or from cron.
  • laidline usage [--month YYYY-MM] [--tenant TENANT]: prints the signed usage statement of a month (default: this month) from the authoritative evidence sink. A statement carries suggested_band: the smallest plan whose monthly figure covers the month's credits (registered_free to 1,200, lite to 15,000, starter to 150,000, growth to 1,000,000, scale to 10,000,000, then enterprise; no prices appear in the package). --tenant needs a licence with tenant_reports; without it the command exits 3.
  • laidline usage report [--month YYYY-MM]: sends the monthly usage report (below). The month must be complete (default: the previous one).

register and licence fetch poll the registration's status without a proof and fetch the documents of an issued registration with one, signed by the active signing key. When the issuing service answers proof_required, both exit 1 with the issuer did not accept this deployment's signing key as the registration's; bind it by running laidline register again (a host clock more than five minutes off, or an issuer.registration_url that is not the issuing service's address, also causes this), and automatic renewal logs the same reason; marking is unaffected. See the documents proof.

register, licence install and licence fetch warn when LAIDLINE_LICENCE is set, because the variable shadows the file just written. Every document is written atomically, so a reload or a concurrent worker never reads a half file. Every command and its options are in the Command line reference.

The usage report

The report ties the allowance to the registration. It holds the month, the payload key_fp per medium, the credits per medium, the counts of private and shared marks, the payload_key_switch event if there was one (the earliest, when replicas each wrote one), the warnings list (for example weights_invalid:licence or weights_unknown:licence when the month's licence carries a table that could not be used; an empty list otherwise; the issuing service keeps it with the report) and the product version. It never holds media, prompts or file names, and it adds no email address of its own. It is signed with the registration's signing key (the same convention as the request) and carries that key's public certificate chain, so the issuing service can tie it to the account; the chain holds whatever your certificate holds (an email address in its subject, for example), and the issuing service receives that.

  • Best effort: marking never waits on it, and nothing is checked online before a mark. A failed send from laidline serve is logged and tried again the next day; the CLI exits 1 and you run it again.
  • issuer.usage_report: false turns it off (laidline usage report then prints that it skipped).
  • laidline serve runs a task that, once a day, sends last month's report when the deployment is registered (an issuer certificate is installed), the setting is on and the usage-reported-<month> marker is absent from the first file sink's evidence directory. The first check runs a minute after start.
  • The issuing service keeps one report per signing key per Month; when a key reports a Month twice, the higher is kept. The usage-reported-<month> marker only saves work in ephemeral containers and on replicas, and the issuer's answer is what decides.
  • It cannot stop a modified worker from lying. The local switch keeps honest deployments honest; the report, the directory and the agreement's audit and True-Up clauses stand against the rest.

Issuer numbers

The issuer number is 1 to 65,535 everywhere. Bit 15 of the 16-bit issuer field, a wire-format constant, splits registered numbers from trial numbers:

Range Numbers Meaning
registered 1 to 32,767 allocated by Laidline with a signed issuer certificate and never allocated to anyone else. It transfers only to a successor of your business under the customer agreement; after the agreement ends, a transfer takes effect no earlier than 90 days after the end, and the directory shows the number as registered to you until the end date throughout that delay
trial 32,768 to 65,279 drawn at random by laidline init; unregistered, shared, no sign-up; trial numbers may collide
reserved 65,280 to 65,535 documentation, tests and benchmarks

No issuer condition ever stops marking. The issuer state is reported: trial, registered, registered_no_certificate, certificate_invalid:<reason> (bad_signature, unknown_signer, malformed, unreadable, number_mismatch, not_bound, revoked), certificate_expired, not_yet_valid, reserved. Detection always shows a payload's number as a claim ("claimed by issuer N, unconfirmed"); the issuer directory adds facts about the number (registered to a name, operator of a domain, per a dated snapshot), never an identity for the mark. The sentences are listed in Issuer lines.

The directory snapshot (laidline.issuer-directory) ships in the package and carries the revocations (licences, issuing keys, issuer certificates) and the shared pool key (Payload keys: The shared family and the private key). laidline issuer refresh fetches a newer one and laidline issuer install FILE installs one by hand. A refreshed snapshot must verify and its serial must not go backwards.

The Laidline roots

Laidline signs with two offline Ed25519 root keys, which sign yearly issuing-key certificates; the issuing keys sign licences, issuer certificates, directory snapshots and weight tables. Either root alone can certify an issuing key. The root public keys are pinned in the package (laidline.trust.roots, the tuple ROOT_KEYS) and copied into every bundle's verify.py.

Root Key id Public key (Ed25519, hex) Not after
A 71541a379b752801 5fe902399350edec70b5038d8d85fb76d107f2e88337827a9fc475938a131fc8 2036-07-28
B c60506fddbdb4685 4965a3e1d6ad06300cfeec5ae4fee087fedecea33cc8c5d2ce2534f3612fd3fa 2036-07-28

The issuing keys (certified by root A) are 88a993b1cc6b3d35 (licences, issuer certificates, weight tables) and c24f4a81f5625273 (the issuer directory only). The directory snapshot in the package is signed by c24f4a81f5625273 and carries one shared pool key, pool-0b19a1bc728c92e5; the weight table 1.0.1 in the package is signed by 88a993b1cc6b3d35. If the directory cannot be verified (for example a refresh was refused), marking uses the configured payload key and the trial number and logs one warning.

Only issuer.extra_roots_file adds a root, and only under LAIDLINE_ENV=development: point it at a roots file ({"roots": [{key_id, public_key, not_after}]}, the shape of an export's licence/laidline-roots.json). Without LAIDLINE_ENV=development the file is never opened.

Troubleshooting

Message Cause Fix
error: registration needs the Laidline customer licence agreement <version> at <url>; read it, then run again with --accept-terms laidline register ran without --accept-terms outside a terminal, or you answered no Read the agreement, then run the command again with --accept-terms.
error: --name is required when system.provider_legal_name is not set The request needs a licensee name Set system.provider_legal_name in laidline.yaml, or pass --name.
error: no active payload key; run laidline init first The configuration has no active payload key to bind Run laidline init, or laidline payload-keys generate and add the key.
error: --display withheld is not offered: ... Registered names are always published Leave --display out; to stay unnamed, stay unregistered on shared marks.
state: licence_invalid:not_bound The licence binds another host's signing key, usually a licence copied without the identity Copy the whole identity (More than one host), or register this deployment.
set watermark.issuer_id: N in laidline.yaml and reload; marks stay shared until then The configured issuer number differs from the number the certificate maps Set watermark.issuer_id to N and reload.
state: licence_expired:grace The renewed licence was not fetched: licence.auto_fetch: false, LAIDLINE_OFFLINE=1, a set LAIDLINE_LICENCE, or no network Run laidline licence fetch REGISTRATION_ID, or install the renewed file with laidline licence install.
credits: unknown (no weight table) No shipped weight table verified (weights_invalid) Reinstall Laidline.
credits: unknown (no evidence sink) The authoritative evidence sink cannot be read Check evidence.sinks in the configuration.
self-test cap reached (10 per day); this mark is metered More than 10 start-up self-test marks in one UTC day: frequent restarts, or a self-test retried while it fails Find why the server restarts or why the self-test fails (/readyz names it); the extra marks are counted as credits.
a per-tenant statement needs a licence with the tenant_reports feature (exit 3) --tenant without a licence carrying tenant_reports Tenant reports come with Enterprise; drop --tenant otherwise.
directory: directory snapshot of <date> is older than <N> days; ... The installed directory snapshot is stale Run laidline issuer refresh.

Reference