Skip to content

Licences and the Community edition ​

reportd checks a signed licence file offline, and grants what the licence lists. Without one, it runs as Community.

What Community includes ​

Community hasCommunity leaves out
5 templates, 1 sub-report per template, 3 designer seatsThe assistant
Passwords with MFA and passkeysSign-in providers (OIDC and SAML)
MCP agents and API keys, and approvals of agents' publishesMore than one environment, and promotion
The viewer and report linksPostgres state, separate renderd instances and job webhooks ("scale")
Branding: your own logo, colours, product name and sign-in page
The example gallery, and importing reports from filesTemplate sources of your own: git repositories, folders and galleries
Batch jobs without webhooks, libraries, the trash, backups and migrations
Every output format and PDF standard

Community output looks like any other: there is no "Made with Reportforge" mark.

A licence lists features and limits:

FeatureGrants
ssoOIDC and SAML providers
environmentsPublishing outside the first environment, and promoting templates, sources and libraries between them
assistantThe studio's AI assistant
scalestate.driver: postgres, renderer.urls and renderer.discover, and job webhooks
brandingThe install's own logo, favicon, colours, product name and sign-in page, in the studio and the viewer
template-sourcesTemplate sources of the organisation's own, which designers copy new reports from: git repositories, folders and published galleries (template-sources.md)
LimitCounts
templatesLive templates; trashed ones don't count
subreportsPerTemplateThe sub-reports a template's rfml.lock.json pins
designerSeatsUsers, not disabled, whose roles (granted or mapped from an IdP) allow template.write in any environment. Viewers and API keys take no seat.

A limit the licence leaves out is unlimited; a limit of 0 means none. A licence's edition ("Professional") is a label to show: reportd never checks it.

Installing a licence ​

A licence is a text block:

text
-----BEGIN REPORTFORGE LICENCE-----
eyJ2IjoxLCJpZCI6ImxpY18wMUo…
.
Wm9tZVNpZ25hdHVyZQ…
-----END REPORTFORGE LICENCE-----

Install it any one of these ways:

  • The studio: the account menu's Licence (administrators, user.manage), then paste it or choose its file, and Install.

  • The setup wizard's licence step, or reportd setup --licence-file <file>.

  • The API: PUT /v1/licence with the block as the body, or {"licence": "<block>"}. DELETE /v1/licence removes it.

  • The CLI, on a stopped reportd: reportd licence install <file>, reportd licence show, reportd licence remove. On state.driver: postgres they work while reportd runs.

  • The licence setting (REPORTD_LICENCE): the block, or a secret: reference to it. It wins over one installed any other way, and the studio then shows it as fixed. On Helm, mount it from a Secret:

    yaml
    secrets:
      - secretName: report-forge-licence
        key: licence
        file: licence
    config:
      licence: secret:file:licence

reportd refuses a licence that doesn't verify, is signed by a key it doesn't know, hasn't started, or has lapsed, and says why. A licence applies at once, without a restart. It is kept in dataDir/licence, or in the licence table on Postgres, so every node sees it. Installs and removals are audited as licence.install <id> and licence.remove <id>.

GET /v1/licence shows anyone with studio.access the phase, features, limits and use. Administrators also see the licensee, the id, the binding, the history, the warnings and what is frozen.

Phases ​

PhaseWhenWhat happens
communityNever had a licenceCommunity's features and limits
transitionAn install from before licences, for 60 daysEvery feature and no limits, with a banner
activeBefore a licence's last 30 daysWhat the licence grants
expiringIts last 30 daysThe same, with a banner and a daily log warning
graceThe 30 days after it expiresThe same, with a stronger banner
lapsedAfter the grace or the transition, or with the licence removedCommunity's limits, with what is in use carried on (below)

reportd checks the phase at start and every hour. Administrators see a banner in the studio in the transition, expiring, grace and lapsed phases, and on a binding mismatch, with the Licence dialog a click away.

The clock ​

Expiry is checked against the latest of the system clock and the newest time in the install's own data: dataDir/licence-clock (reportd writes the time there every hour), the newest audit entry (across the rotated logs, and on Postgres the audit_events table, so rotation can't move the clock back), the newest commit on an environment branch, and the newest draft edit and job in the template store. So:

  • setting the system clock back doesn't bring an expired licence back;
  • editing or deleting licence-clock does nothing while the data holds later times;
  • a system clock more than a day behind the data is a warning in the log and the studio. New records still get the system's time.

Installs from before licences ​

The release that brings licences takes dataDir to format 6 (migrations.md). On an install that already has users or templates, that step records a 60-day transition in dataDir/licence-history (and, on Postgres, in the schema's licence table, from the users there). For those 60 days every feature works and nothing is limited, and:

  • administrators see a banner with the days left, and what Community would do: which templates it would freeze, how many designers over 3, and which configured features need a licence;
  • the log says the same at start and daily.

After the 60 days the install is lapsed, as below. Installing a licence at any point ends the transition. The end date is checked against the latest time, so turning the clock back doesn't extend it.

A new install, with an empty dataDir, records no transition and is Community from its first start.

Restoring a backup from before the upgrade starts a new transition, because the restored data holds no record of the first, and nothing else can tell. It costs everything since that backup, so it isn't a practical way round the limits.

Lapses and features in use ​

A lapse never stops reportd or locks anyone out. It stops growth: new templates past the limit, more designers, new people through SSO.

A feature in use keeps working after a lapse only if a licence this install had granted it, or the install had the 60-day transition. A feature no licence of the install granted is treated as on an install that has never had a licence.

FeatureLapsed, and a licence the install had granted itNever granted
SSOProviders keep signing in the people they already sign in. JIT makes no one, no invite binds, and no identity is linked (the sign-in page says a licence is needed).Needs a licence with sso while auth.oidc or auth.saml has providers.
EnvironmentsEvery environment keeps rendering what it has. Publishing, rollback and library releases work only in the first environment; promoting is refused. An approval that would publish elsewhere stays pending.The same, and reportd logs at start which environments are ignored.
AssistantOff at onceOff
Postgres stateKeeps working, with a warningNeeds a licence with scale while state.driver is postgres.
Separate renderdKeeps spreading renders, with a warningNeeds a licence with scale while renderer.urls or renderer.discover is set.
WebhooksExisting endpoints keep delivering. A new job can't name a callbackUrl.jobs.webhooks is ignored, with a warning.
BrandingKeeps showing, frozen: it can't be changed until a licence grants branding again.The defaults show, and branding can't be set.
Template sourcesKept as they were set up, but off: designers see only the example gallery. They come back on with a licence that grants template-sources. Reports copied from them are untouched.Only the example gallery can be turned on or off; adding a source is refused.

dataDir/licence-history (and, on Postgres, the schema's licence_seen table) remembers each licence installed and the features it granted. It survives restores of later backups, and removing a licence never clears it. So:

  • a licence with scale that lapses, or is replaced by one without scale, leaves Postgres state, separate renderd instances and the webhooks working;
  • a licence without scale never unlocks them, whether it is installed, in its grace, or lapsed;
  • the transition counts as granting every feature, for good;
  • licences recorded before reportd recorded features count as granting every feature, so an install upgraded with one keeps what it has.

The break-glass administrator's password always signs in (sso.md).

A configuration that needs a licence ​

An install with SSO providers, Postgres state or separate renderd instances configured, and no licence now or in its history that granted the feature (sso or scale), still starts. That is an install that has never had a licence, or one whose licences didn't grant the feature. It logs what it needs (state.driver postgres needs a licence with scale, for example), then asks for the licence:

  • In setup mode (no users yet), the setup wizard's licence step needs a licence that grants what the configuration needs. "Continue with Community" isn't offered, and setup can't finish without one. reportd setup checks the same.
  • With users (for example a Community install that has since been given state.driver: postgres), only signing in, GET /v1/me, GET /v1/info, POST /v1/logout and /v1/licence work. Every other request answers 503 licence-needed, naming what is needed. The studio shows "This install needs a licence", and administrators install one from it. Once a licence is installed, everything works at once, without a restart.

The licence can also be given before the first start: the licence setting (REPORTD_LICENCE), or reportd licence install on a stopped reportd.

PUT /v1/licence, reportd licence install and the Licence dialog accept any licence that verifies, but one that doesn't grant what the configuration needs changes nothing: the install keeps answering licence-needed until a licence with the feature is installed. Once one is, the feature keeps working after that licence lapses, as above.

Limits ​

Each refusal is 409 licence-limit:

json
{"error": "licence-limit", "limit": "templates", "allowed": 5, "used": 5,
 "message": "Community allows 5 templates, and this install has 5. Delete a template, or install a licence that allows more."}

and a missing feature is 403 {"error": "licence-required", "feature": "assistant", "message": …}. The studio says what a licence unlocks where it offers these: the New report button, the assistant panel, Promote and publishing outside the first environment.

  • Templates are checked when one is created (a draft of an existing template in another environment isn't new), restored from the trash, or seeded; seeding stops at the limit and logs what it skipped.
  • Over the limit, after a downgrade or a lapse, the oldest templates, by their first draft edit, stay editable; the rest are frozen. A frozen template's drafts can't be saved, and it can't be published or promoted, but every published version keeps rendering. The catalog marks it Frozen, and a designer who opens it sees why. Deleting templates, or installing a licence, unfreezes them at once.
  • Sub-reports: a publish (dry runs too) or a promote whose lock pins more sub-reports than subreportsPerTemplate is refused. Versions already published keep rendering.
  • Seats: creating a user, granting a role, enabling a user or giving a custom role template.write is refused when it would make a designer past designerSeats. Existing designers keep their roles when the seats shrink; removing or disabling designers frees seats. An SSO sign-in whose mapped roles would make a new designer past the seats still signs in, without the roles that allow template.write; it is audited as licence.seat-refused <name>, and the studio tells the user.

Binding ​

A licence can name an installation id and domains. A mismatch is a warning in the log and the studio, never a refusal.

  • The installation id is made at first start and kept in dataDir/installation-id (on Postgres, shared by the nodes). Backups include it, so a restore keeps it. reportd licence show and the Licence dialog show it; send it with a licence request.
  • Domains are checked against publicURL's host; *.example.com covers its subdomains.

Files ​

FileHolds
dataDir/licenceThe installed licence (state.driver file)
dataDir/licence-historyThe licences installed, with the features each granted, and the transition
dataDir/licence-clockThe latest time, written hourly
dataDir/installation-idThe installation id

Backups include all four. On Postgres the licence, the history and the installation id are also in the schema's licence and licence_seen tables.