Self-Hosted License (cred.json) Runbook¶
This runbook describes how a self-hosted customer's license is created, tested, upgraded, and delivered. It covers the license definition inside the migration templates, the Google Cloud service account and bucket setup behind cred.json, and the end-to-end flow for onboarding a new customer.
Most of the license work is driven from the appcircleio/ac-script-self-hosted repository, on the customer's deploy/<company-name> branch. This runbook complements the Self-Hosted Server Release Runbook, which covers the release and deploy-branch update flow.
Overview¶
Two distinct artifacts are involved, and it helps to keep them separate:
- The license — the enterprise license values (plan, start date, end date, limits) defined inside the Keycloak migration templates in
ac-script-self-hosted. These values are what enforce the customer's plan and limits. cred.json— a Google Cloud service-account key used to pull the customer's packages and to authenticate license retrieval. Each customer has a dedicated service account and a private bucket folder.
License changes are always made on the customer's deploy/* branch, and the resulting package is published automatically once the change is committed.
License Cases¶
There are three cases to handle:
- License purchased, everything defined. The plan and limits are clear; create or set the license directly.
- License purchased but not fully clear, or an existing license needs updating. This case has become more common recently. Apply the change as a license update (see Updating a License).
- PoC with raised limits. A proof-of-concept where the customer wants some limits increased.
Where the License Is Defined¶
The initial license lives in the Keycloak migration templates:
Config -> keycloak -> migrations -> v1.0.0-secret.json.tmpl
The enterprise license block is around line ~3500 of that file. When creating or changing a license:
- Edit the enterprise license section.
- The start date and end date are critical values.
- The generated secret is important and must be handled carefully.
License template versions start at v1.0.0 and are separate from the main migrations. Do not confuse the license version sequence with the main migration sequence.
Creating the First License¶
For a brand-new installation, the license is set in v1.0.0:
- Set the enterprise license values, including the correct start date and end date.
- If new license fields exist, make sure they are present in
v1.0.0(see New License Fields).
For the Helm chart, use the vX.X.X-license.json.tmpl template in the same way.
Updating a License¶
License updates proceed through a patch migration:
- Go to the migrations directory on the customer's branch.
- Increment the patch part of the last migration version.
- Copy the
vX.X.X-license.json.tmplfile to its actual location, changing only the version number. - Enter the values from the most recent license into this new template.
- Apply the required license update on top of those values.
Expired Licenses¶
For a license that has expired:
- Keep the original (older) start date and carry it forward. This is safer: if the package is installed in another environment, the license does not restart from scratch.
- Changing only the end date is sufficient.
Warning
Do not reset the start date on a renewal. Preserving the original start date prevents the license period from restarting when the package is installed in a different environment.
New License Fields¶
When new license fields are added, they must also be added to the template and to v1.0.0, and tracked over time.
Warning
This is a self-hosted-only case: some license fields exist in self-hosted but not in cloud. Because of that, new fields must be tracked carefully so they are not missed in the self-hosted templates.
Reference example: BE-4938 "New license metric" — PR-229 is the ideal example to follow.
Keycloak internal migrations can also be used to update customers' licenses. Always work through a Linear issue when doing this.
The deploy/* Branch Workflow¶
License operations are performed on deploy/* branches, and commits are pushed directly to those branches:
- Test locally first, before committing. Run the tests on your local machine without committing.
- After the tests pass, commit and push.
- Once the commit lands, the package is produced automatically and becomes available to the customer.
Other rules for these branches:
- Deployments are performed by merging
master->deploy/*branches. - Hotfixes that are not yet GA are also applied to customer branches, and may be removed later. Track these hotfixes.
- A branch protection rule is configured for
deploy/*.
Google Cloud Service Account and Bucket¶
Service Account¶
- Service account names start with
self-hosted. The naming convention isself-hosted-<uuid>. - Generate the UUID with uuidgen.org/v4.
- The description must contain the customer name.
- The service-account email follows the
self-hosted-<uuid>form.
Permissions:
- Artifact Registry Reader — required to pull images.
- Storage Object Viewer for the offline packages zip, applied conditionally (not needed for a Helm chart install). Do not forget the storage object viewer condition.
Key management:
- Service-account keys are created without an expiry date (no expiration).
- When a license expires, the service-account key is revoked.
- Each customer should have exactly one key.
- A service account with multiple keys is for developers: when a new developer joins, they use that service account to set up self-hosted.
- Google Cloud service accounts can be filtered in the console to find the right one.
Bucket¶
The license and packages live in the appcircle-self-hosted bucket, under the customer's UUID folder, inside the license folder.
To set up the folder:
- Go into the bucket and create a folder with the same UUID.
- Create an empty
index.txt(for example withtouch) and upload it to that folder. - On the UUID folder, open the three-dot menu and choose Edit access.
- When prompted to make it a managed folder, choose yes.
- You may disable inherited permissions.
- Choose Add principal, add the service account you created, and grant it Storage Object Viewer.
License Testing¶
- For testing, an upgrade is usually done instead of a fresh install.
- Delete the intermediate patch packages, keep only the last license patch, then run the upgrade and observe the result.
- For the test, a temporary
cred.jsonis generated on that customer's service account. Delete it after the test is complete.
Danger
If you delete the wrong key, do not revoke the customer's real cred.json. Revoking the customer's active key breaks their access.
Custom Images¶
Custom images are removed (and the change is committed) as part of setting up the customer branch.
Private Cloud Storage License¶
For private-cloud customers, pay attention to the storage license:
- The default is 5 TB and must be changed to match the customer.
- The server disk must be sized accordingly.
Take the compatible version from Docker/Podman and apply it the same way as the template above.
Sample Flow — Onboarding a New Customer¶
When a new customer arrives:
- Create the IAM service account.
- Service-account names start with
self-hosted. - Name:
self-hosted-<uuid>. Generate the UUID from uuidgen.org/v4. - Description: the customer name.
- Permissions:
- Artifact Registry Reader.
- Storage Object Viewer for the offline packages zip, applied conditionally (not needed for Helm chart). Do not forget the storage object viewer condition.
-
A service account with multiple keys is reserved for developers (used when a new developer joins to set up self-hosted).
-
Set up the bucket folder.
- Go into the bucket and create a folder with the same UUID.
touchan emptyindex.txtand upload it there.-
Open the three-dot menu on the UUID folder and choose Edit access.
- Confirm yes when asked to make it a managed folder.
- You may disable inherited permissions.
- Add principal -> the service account you created -> Storage Object Viewer.
-
Prepare the Git branch.
- In the repository, branch from
master:deploy/<customer_name>(use a user-friendly name). - Create the
.UUIDfile and commit it (do not push yet). -
Remove the custom images and commit (do not push yet).
-
Set the license.
- First-time installation ->
v1.0.0. - If the customer already installed a PoC package -> apply the license upgrade.
-
Commit the license change.
-
Push the branch.
Once the branch is pushed and the commit lands, the package is produced automatically and becomes available to the customer.