VulnScanningEnabled reads GetLicenseState(...).Feature("vuln_scanning")
and requires an active licence, never switching on tier. ReportPackages
re-checks it server-side: the agent flag is the optimisation, this is
the boundary.
The pb packages are hand-written, not protoc-generated, and the wire
codec is JSON (encoding.RegisterCodec(JSONCodec{})). Field numbers in
the .proto are documentation; JSON field names are the contract. Both pb
packages edited by hand to match.
SyncResponse.collect_packages is omitempty and absent decodes as false,
so an older server leaves agents collecting nothing rather than
collecting without a licence.
Monitor execution and the workflow runner are deliberately unguarded: billing
state must not take away a customer's ability to know their infrastructure is
on fire, and killing a run midway leaves a half-configured server.
The plan called for a server-limit check in gRPC Register. Left out: a server
row only comes from CreateServer, which already checks the cap, so counting in
Register counts the caller itself and would reject a legitimate agent at
exactly the cap.
Adds migration 0004_org_to_instance, the ScopedCollections list, the
AssertNoScopedCollectionMissed boot check, and moves EnsureAuthIndexes into
its own file.
Two ordering constraints the rename exposed, both now enforced and commented:
- 0004 must run BEFORE EnsureAuthIndexes. The index builder creates
instances.slug, which would create an empty instances collection and make
0004 refuse to rename orgs onto an existing target.
- Migrations 0001 to 0003 run BEFORE 0004 and still read and write org_id, so
they use a private legacyOrg struct rather than shared/models.
Two instances of the branch's recurring bug class remained in the agent
path: a client-supplied ID accepted as data, then consumed by an
unscoped query.
- ListMonitorsForRunner filtered on `runner` alone, and `runner` is set
by the client on monitor create/update. Org A could point a monitor at
org B's server_id and org B's agent would fetch and execute the check.
Now org-filtered, and `runner` is validated against the caller's org on
create and update.
- IngestResult resolved the monitor via the unfiltered getMonitorByID
using a monitor_id from the agent's request body, letting org A's agent
write state and incidents into org B's monitor and fire its channels.
Now rejects on org mismatch and on a monitor not assigned to the
reporting agent.
The in-process scheduler passes an empty orgID as a documented sentinel
for the cross-org server-run sweep.
Install script still emits a single shared GRPC_HOST; the agent path
resolves org from the server record, never from a hostname.
Threads org_id through every admin-facing service function (servers, keys,
assignments, secrets, workflows/steps/runs, monitors, channels, audit),
adds RequireRole middleware, and wires /api/org user + OIDC management
routes. Agent/scheduler paths keep unique-key signatures and resolve org
from the loaded record; internal-only helpers (getServerByID,
getRunByID, getMonitorByID) preserve those call sites.