Licensing
This page describes how a Foundation4 license is bound to an installation, how operators read the system ID, apply and renew a license, and what the license limits. The page also describes how Foundation4 behaves when the license is invalid, expired or exceeded. Operators and administrators who install or run Foundation4 use this page.
The commands on this page read the production values file foundation4ai.values.yaml, created in Install on Kubernetes. An evaluation installation uses evaluation.values.yaml instead.
License binding
Every Foundation4 installation requires a license, including an evaluation installation. The Foundation4 provider issues each license for one system ID. The system ID identifies one PostgreSQL database.
Foundation4 computes the system ID from PostgreSQL catalog object identifiers (OIDs) of the public schema, the owner of that schema and the api_keys table. PostgreSQL assigns these identifiers when the database and the Foundation4 tables are created. The system ID therefore exists only after the database migration has created the schema, and the value is a string of 64 hexadecimal characters.
| Event | System ID | License |
|---|---|---|
| Pod restart, Helm upgrade, application release reinstalled against the same database | Unchanged | Remains valid |
| New database, including a new installation | New | New license required |
| Bundled PostgreSQL pod deleted or rescheduled (evaluation profile) | New, because the database is recreated empty | New license required |
Logical restore with pg_dump and pg_restore, into any database | New | New license required |
| Physical restore, storage snapshot, or failover to a physical replica | Expected to be unchanged; not yet tested | Confirm with the license check log |
public schema assigned to a new owner, or api_keys table recreated | New | New license required |
The following rules keep the system ID stable:
- Default schema names.
database.schemakeeps the default,public. The installation Jobs and the command-line tools compute the system ID from thepublicschema, while the API server and the workers usedatabase.schema, so a changed schema name produces two different system IDs. - Schema ownership. The owner of the
publicschema does not change after the license is installed.
Database describes the database requirements, and Backup, restore and upgrades describes restores.
System ID retrieval
The system ID is available from three sources:
- License check Job log. The Job
foundation4ai-api-server-license-checkprints the system ID in the first line of the log, at every installation and upgrade of the application release. This source is the only one available during the first installation. - Command in a running pod.
foundation4ai license system-idprints the system ID of the database that the pod is configured for. - Startup warning. When the license does not match, the API server and the workers log a warning with the system ID before exiting.
Read the system ID from the license check Job log:
kubectl logs -n foundation4ai job/foundation4ai-api-server-license-check
Expected result: the first line holds the system ID, and the second line holds the license status.
SystemID: <system ID>
Checking for a valid license... Invalid
Read the system ID from a running API server pod:
kubectl exec -n foundation4ai deploy/foundation4ai-api-server -c server -- \
./foundation4ai license system-id
Expected result: one line with 64 hexadecimal characters.
Find the startup warning in the log of an API server that exited:
kubectl logs -n foundation4ai deploy/foundation4ai-api-server -c server --previous | \
grep "Failed to validate license for system id"
Expected result: a WARN line that ends with Failed to validate license for system id: <system ID>.
License contents
foundation4ai license details prints the contents of a license after checking the license against the system ID of the database:
kubectl exec -n foundation4ai deploy/foundation4ai-api-server -c server -- \
./foundation4ai license details '<license>'
Expected result for a license that matches the database, with each value in angle brackets replaced by the license contents:
License: <license holder> (expires on <date> <time> UTC)
System ID: <system ID>
Version: 2
Limits:
Pipelines: <limit>
Documents: <limit>
The output has the following parts:
- Holder and expiry. The first line names the license holder and the expiry date and time in UTC, or
(perpetual)for a license without an expiry date. - Optional lines.
Description:andEnvironment:lines appear when the license carries these fields. - Limits. One line for each limited object type:
Pipelines,Documents,Fragments,Text Splitters,Embedding Models,Agents,LLMs,Api KeysandTaxonomies. An object type without a line has no limit, andnonemeans that no object type is limited. - Errors. A single word replaces the details when the license cannot be used:
Invalidfor a license that is malformed, of an unsupported version or issued for another system ID, orExpired.
License installation
The first installation obtains the system ID, then installs the license. The steps are part of Install on Kubernetes and of steps 4 to 6 of Install for evaluation:
- The secrets file carries the placeholder
pendinginFOUNDATION4AI_APP_LICENSE. - The first application install migrates the database, then stops at the license check, which prints the system ID and waits.
- The operator sends the system ID to the Foundation4 provider and receives the license.
- The operator sets the license in
foundation4ai.secrets.env, applies the Secret, upgrades the core release, deletes the waiting license check Job and installs the application release again.
The license check Job waits for 365 days after reporting a failed check, so that the system ID stays readable in the log. Helm therefore stops the install at the Helm timeout, and the Job keeps running after Helm stops. The Job is deleted before the next install or upgrade of the application release.
License renewal
A renewed license replaces the license of a running installation, for example before the expiry date or when the limits change. The renewed license is checked first, because a license that fails the check stops every restarted pod.
-
Check the renewed license against the database:
kubectl exec -n foundation4ai deploy/foundation4ai-api-server -c server -- \./foundation4ai license details '<renewed license>'Expected result: the license details, starting with
License:, and the expected expiry date and limits.InvalidorExpiredmeans that the license cannot be used; the procedure stops here. -
Replace the value of
FOUNDATION4AI_APP_LICENSEinfoundation4ai.secrets.envand apply the Secret:kubectl kustomize . | kubectl apply -f -Expected result:
secret/foundation4ai-secrets configured. -
Upgrade the core release, which copies the license into the Secret
foundation4ai-core:helm upgrade foundation4ai-core ./charts/foundation4ai-core \-n foundation4ai -f foundation4ai.values.yaml --wait --timeout 10mExpected result: Helm reports
STATUS: deployed. -
Upgrade the application release, which copies the license into the configuration of the pods and runs the license check:
helm upgrade foundation4ai ./charts/foundation4ai \-n foundation4ai -f foundation4ai.values.yaml --wait --timeout 10mkubectl logs -n foundation4ai job/foundation4ai-api-server-license-checkExpected result: Helm reports
STATUS: deployed, and the license check log ends withChecking for a valid license... OK. -
Restart the API server and the workers, which read the license only at startup:
kubectl rollout restart -n foundation4ai \deployment/foundation4ai-api-server deployment/foundation4ai-api-server-workerkubectl rollout status -n foundation4ai deployment/foundation4ai-api-serverkubectl rollout status -n foundation4ai deployment/foundation4ai-api-server-workerExpected result: both Deployments report
successfully rolled out, and the new pods areRunning.
Foundation4 reports no warning before a license expires. The operator records the expiry date of each license and renews the license before that date.
License enforcement at startup
Every process that opens a full Foundation4 connection reads the license at startup: the API server, each worker and the master key Job. The database migration Job does not read the license. A license that is malformed, issued for another system ID, of an unsupported version or expired stops the process. The error names the license, but the message starts with a database connection error:
| Process | Output |
|---|---|
| License check Job | Checking for a valid license... followed by Missing, Invalid or Expired, then the Job waits |
| Master key Job | Error: "Failed to create database connection: Invalid license" |
| API server | Error: "Failed to initialize server: Failed to create database connection: Invalid license" |
| Worker | Error: "Failed to connect to Foundation4.ai: Invalid license" |
An expired license replaces Invalid license with Expired license. The API server and the workers also log the warning Failed to validate license for system id: <system ID>.
A license expiry does not stop running pods. A pod that restarts after the expiry, after a node failure or during a rolling update, does not start again until a valid license is installed.
HTTP 401 on POST /login is a credential error, never a license error. A license error appears either at startup or as HTTP 403.
License enforcement at runtime
The API server checks the license before every operation that creates or changes data:
- Checked operations. Creating and updating pipelines, text splitters, embedding models, agents, LLMs, API keys and taxonomies; creating and expiring documents; changing classifications, indexes, taxonomy entries and API key permissions; direct LLM queries.
- Unchecked operations. Reads, searches and deletes. Agent execution and the agent retrieval dry run also run without a license check.
- Workers. Each worker checks the expiry date before processing the documents of a pipeline. After the expiry, documents remain
pending, and queued jobs are removed after 24 hours.
A refused operation returns HTTP 403 with one of the following messages. Errors describes each message.
| Message | Condition |
|---|---|
Invalid license: Expired | The license has expired |
Invalid license: Exceeded("<types>") | A cached count exceeds the limit for the named object types; every checked operation fails, whatever the object type |
License limits exceeded for <type> | Creating one more object of the type would exceed the limit |
The API server counts objects in the following ways:
| Object types | Count | Refresh |
|---|---|---|
| Pipelines, text splitters, embedding models, agents, LLMs, API keys, taxonomies | Exact count | At API server startup and every 10 minutes; each create also counts exactly |
| Documents, fragments | Estimate from PostgreSQL table statistics, after ANALYZE | At API server startup and every 3 hours |
Deleting objects lowers the counts at the next refresh. Restarting the API server refreshes every count at once. The workers keep no counts and check only the expiry date.
After a renewal, the client application submits again the documents that remain pending. Ingest documents reliably describes resubmission.
Troubleshooting
Installation or upgrade waiting at the license check
- Symptoms.
helm installorhelm upgradeof the application release stops at the timeout with a failed pre-install or pre-upgrade hook, and the pod offoundation4ai-api-server-license-checkstaysRunning. - Diagnosis.
kubectl logs -n foundation4ai job/foundation4ai-api-server-license-checkshowsChecking for a valid license...followed byMissing,InvalidorExpired. - Cause. The license in
foundation4ai-coredoes not match the system ID, has expired, is empty or is still the placeholder. The Job waits for 365 days so that the system ID stays in the log. - Resolution. Obtain a license for the system ID in the log, apply the Secret, upgrade the core release, and delete the Job with
kubectl delete job -n foundation4ai foundation4ai-api-server-license-check. Then install or upgrade the application release again. - Actions to avoid. Repeating the application upgrade before the core release is upgraded. The core release copies the license, so the repeated upgrade checks the previous license again.
API server or workers exiting with a database connection error
- Symptoms. The
serverorworkercontainers restart repeatedly, and the logs reportFailed to create database connectionorFailed to connect to Foundation4.ai. - Diagnosis.
kubectl logs -n foundation4ai deploy/foundation4ai-api-server -c server --previousshows whether the message ends withInvalid licenseorExpired license, and shows the warning with the system ID. - Cause. The license is not valid for the database. The database was replaced or restored, the license expired, or a restart after the expiry could not start again.
- Resolution. Obtain a license for the system ID in the warning and follow License renewal from step 2.
- Actions to avoid. Recreating the database or reinstalling the core release to clear the error. A new database has a new system ID and needs a new license as well.
License rejected for a license issued for the installation
- Symptoms.
license detailsor the license check printsInvalidfor a license that the Foundation4 provider issued for this installation. - Diagnosis.
kubectl logs -n foundation4ai job/foundation4ai-api-server-license-checkshows the current system ID. The system ID sent to the provider is compared with the current value. - Cause. The system ID changed after the license request: the database was recreated, restored or assigned a new schema owner. A license copied with missing or extra characters also reads as invalid.
- Resolution. Copy the license again without line breaks. When the system ID differs, request a license for the current system ID.
- Actions to avoid. Changing
database.schemato match a system ID. The API server then computes the system ID from a different schema than the license check.
Writes refused with HTTP 403 while the license is current
-
Symptoms. Create and update requests for several object types return HTTP 403 with
Invalid license: Exceeded("<types>"), although the license has not expired. -
Diagnosis. The following command prints the limits of the installed license. The number of objects of each named type, from the list endpoints of the API, is compared with the limit.
kubectl exec -n foundation4ai deploy/foundation4ai-api-server -c server -- \./foundation4ai license details \"$(kubectl get secret foundation4ai-secrets -n foundation4ai -o jsonpath='{.data.FOUNDATION4AI_APP_LICENSE}' | base64 -d)" -
Cause. The cached count of the named object types exceeds the license limit. For documents and fragments, the count is an estimate that is refreshed every 3 hours.
-
Resolution. Delete objects of the named types and restart the API server to refresh the counts, or install a license with higher limits.
-
Actions to avoid. Creating objects of other types to test the license. Every checked operation fails while any count exceeds the limit.
Documents pending after a license renewal
- Symptoms. Documents submitted before or during the expiry remain
pendingafter the renewal. - Diagnosis. The client application finds the documents that remain
pending, as described in Ingest documents reliably. Before the workers are restarted,kubectl logs -n foundation4ai deploy/foundation4ai-api-server-worker -c worker | grep "Expired license"shows lines such asError processing documents for pipeline <pipeline ID>: Expired license. - Cause. Workers stop processing after the expiry. Queued jobs are removed after 24 hours, and the documents keep the status
pending. - Resolution. The client application submits the documents again, as described in Ingest documents reliably.
- Actions to avoid. Restarting the workers to process the old jobs. Jobs removed from the queue are not recovered by a restart.