Pulsobot security pack

Licence model

Security pack

An offline, signed file

A Licence is a small JSON file signed with Ed25519 by Pulsover. It names the customer, the Databricks account ID, the install it is issued to (Install Key fingerprint), the Tier, optional features, the maximum number of Accounts, the issue and expiry dates and the Grace Period length (domain/licence/licence.go).

Where it is checked

Where What it gates
API App (re-read every 5 minutes) Paid routes: submitting, retrying, validating and promoting Remediation Orders; LLM re-analysis; Monitor; the number of active Accounts. Anything else answers 403 (middleware.RequireLicence)
Remediation Agent, Test Agent (at start-up) Without an Optimize Licence they are dormant: they log it, take no work and exit successfully
Deployment Agent (at start-up) Without an Optimize Licence it starts no new deployment; its safety sweeps (pause, rollback, teardown) still run
Rules job Paid rules are evaluated only with an Optimize Licence

The job checks exist because anyone with workspace access could trigger a job directly. Remediation also has a second, independent gate: the FF_REMEDIATION feature flag. Both must allow a write; the Licence never replaces the flag. Hiding menus in the dashboard is a convenience; the server-side checks are the boundary.

Any error while checking (secret unreadable, keyring invalid) fails closed for new work, never for work already in flight.

Expiry, Grace Period and read-only fallback

  1. Until expires_at: everything the Tier allows.
  2. Grace Period (grace_days after expiry): everything keeps running; the dashboard shows a banner.
  3. After the Grace Period: the install falls back to the Diagnose Tier, read-only for remediation. No new Remediation Orders, no new tests, no new deployments; the paid routes answer 403.

Only the agents that start new work check the Licence: the Remediation and Test Agents at startup, the Deployment Agent before claiming a deployment. Without a valid Optimize Licence they log dormant and exit 0. Work already in flight always finishes: the Verification Agent and the Cleanup Agent never check the Licence, rollback and pause sweeps keep running, and cancel / revert routes stay open (test/architecture/licence_test.go). An expired Licence never leaves a half-done change on your jobs.

Signing keys, kid list and rotation

Each public key in the binary's keyring has a key ID (kid) and a purpose:

Purpose Can sign Where the private key lives
licence Licences only AWS KMS (Ed25519); only the sign-licence workflow's role, behind a human approval, may sign
bundle Paid Rule Set Bundles and the free-template manifest, never a Licence AWS KMS; only the promote-ruleset workflow's role, behind a human approval, may sign
free Free Rule Set Bundles only, never a Licence or a paid bundle AWS KMS; only the registration service may sign

An install accepts a signature only from a key of the expected purpose. Each key has exactly one IAM role allowed to sign and an explicit Deny for everyone else, asserted in CI by tofu test (infra/licence/tests, infra/registration/tests).

Private keys never leave AWS KMS.

Residual risk: key policy changes. KMS key administrators keep kms:PutKeyPolicy (without it Terraform could lock itself out of a key), so a key administrator could rewrite a policy to grant themselves kms:Sign. Mitigation: few key administrators, and a CloudTrail alert (EventBridge rule on PutKeyPolicy, CreateGrant and ScheduleKeyDeletion for these keys) that notifies the security owner. Any such change also shows up as drift on the next tofu plan. Rotation: a new public key ships in a release next to the old one; once customers have that release, signing switches to the new key and the old entry gets a retired_at date (a Licence it signs must have been issued before that date, reason retired_kid otherwise; the signing tools refuse a retired key for every artifact); the old key is dropped once no Licence it signed is still valid. A release check (pulsobot-licence keyring check) refuses a keyring change that would strand valid Licences. If a key is ever compromised, it is disabled in KMS at once and dropped from the keyring in a security release; still-valid Licences are re-signed with the new key and re-delivered.

Revocation scope

There is no remote kill switch, by design (no call home). What Pulsover can do:

What Pulsover cannot do: delete or disable anything in your workspace, or make a delivered bundle unreadable. Bundles already delivered stay readable and usable by that install.

Rule Set Bundles

Rules reach an install only as a Rule Set Bundle: ruleset.json plus every SQL and LLM template of one Rule Set version. No binary embeds a rule template (the release workflow scans every archive for them). Each bundle is:

The free bundle comes from the registration service, which takes no Tier input, signs with the free key only, and checks its templates against a manifest signed by the bundle key, so it cannot introduce SQL that Pulsover did not approve. Paid bundles and Licences are produced only by a human-approved promotion workflow.