Commit Graph
496 Commits
Author SHA1 Message Date
mrhid6andClaude Opus 5 b8fcf89ee7 feat(admin): documents, indexes and the plan seed
Adds admin's own documents, its unique indexes and the plan seed from
shared/license.

Licences are append-only -- a renewal writes a new row and supersedes the
old one -- because the history is the support tool. Plans are seeded with
$setOnInsert only, so a redeploy never stamps over staff edits to limits,
features or Paddle IDs.

admin_instances.instance_id unique is a correctness property, not an
optimisation: without it two customers could both claim one self-hosted
UUID and both be issued licences for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 19:00:37 +01:00
mrhid6andClaude Opus 5 64eac6dbc7 feat(admin): module skeleton, config and two database connections
Adds the admin module: env config with fail-fast validation, two MongoDB
connections (its own vantage_admin database plus a narrow path into the
control plane), the boot sequence and the image.

Config refuses to start without a signing key, and both Mongo URIs must
name their database inline -- admin talks to two databases, so a bare
MONGO_DB would be ambiguous about which.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 18:58:44 +01:00
mrhid6 c4f1684304 docs(plan): drop backfill, add staff instance attach
Server Deploy / deploy (push) Failing after 37s
Existing cloud instances get licensed by hand through the admin UI instead of
an automated backfill. That needs POST /api/staff/instances, which nothing else
provided — without it there is no way to attach an existing cloud instance to
an account.
2026-07-24 16:08:29 +01:00
mrhid6 8ed3bec511 docs: mark plans 1 and 2 shipped 2026-07-24 16:00:45 +01:00
mrhid6 58faf2e57f docs: add the admin-backend implementation plan 2026-07-24 16:00:37 +01:00
mrhid6 8e5f35f40c revert(server): drop the licence grandfather migration
Instances stay read-only until a licence is set. No licence is generated by
the control plane, which keeps the signing key out of it entirely.
2026-07-24 15:49:46 +01:00
mrhid6 eb8b85ccfe fix(server): add the lk go.sum entry the workspace was masking
Server Deploy / deploy (push) Successful in 2m21s
go build inside the Docker image runs outside the workspace, so server/go.sum
needed the hyperboloide/lk entry that GOWORK resolution was supplying locally.
Caught by the image build, not by go build.
2026-07-24 15:35:01 +01:00
mrhid6 09a39090c6 feat(server): grandfather existing cloud instances onto Professional 2026-07-24 15:26:26 +01:00
mrhid6 8626898e5e feat(web): licence banner, settings page and feature gating 2026-07-24 15:25:29 +01:00
mrhid6 ee4dff09c9 feat: show the instance ID after setup 2026-07-24 15:20:00 +01:00
mrhid6 8d88b16f20 feat(server): keep monitors and in-flight runs going when a licence lapses
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.
2026-07-24 15:15:06 +01:00
mrhid6 120c9c6735 feat(server): licence API, mutation gate and feature gates
RequireActiveLicense is mounted on the /api group so new routes are gated by
where they live. GET /api/servers/new is named explicitly: it mints a
pre-registration token, so it mutates despite the method.
2026-07-24 15:13:35 +01:00
mrhid6 855537c535 feat(server): enforce licence limits on servers, secret groups and channels 2026-07-24 15:11:03 +01:00
mrhid6 1d3fcebb28 feat(server): resolve licence state per instance 2026-07-24 15:09:47 +01:00
mrhid6 f9f382049d feat(shared): add licence fields to Instance 2026-07-24 15:08:48 +01:00
mrhid6 e4d7569a1c refactor(server): correct stale org wording in messages 2026-07-24 15:07:54 +01:00
mrhid6 c3e363eccc refactor(server): finish the Org to Instance rename
Private identifiers plan 0b's naming map missed, plus the OrgOIDC model type.
No wire format, database field or route changes.
2026-07-24 15:07:06 +01:00
mrhid6 6fde319b2f feat(license): trust the production signing key 2026-07-24 15:01:03 +01:00
mrhid6 4f1fce32c1 feat(license): add lkctl for issuing licences by hand 2026-07-24 14:59:13 +01:00
mrhid6 968408b955 feat(license): add offline verification 2026-07-24 14:58:25 +01:00
mrhid6 95c5d531ae feat(license): add signing and the trusted key list 2026-07-24 14:57:52 +01:00
mrhid6 b0d8edf9b6 feat(license): add the licence payload and tier seed table 2026-07-24 14:57:21 +01:00
mrhid6 a50219c0d9 docs: correct licence scheme to ECDSA P-384 with SHA-256
hyperboloide/lk signs with ECDSA P-384 and SHA-256, not ed25519, and encodes
keys as base32 rather than hex. Probed in task 1 of the licensing-core plan.
Design is unaffected — only the prose was wrong.
2026-07-24 14:56:36 +01:00
mrhid6 f10fe61916 chore(shared): add hyperboloide/lk for licence signing
Probed the library before building against it. Two corrections to plan 1:

- PublicKey.ToB32String() returns one value, not (string, error)
- The scheme is ECDSA P-384 with SHA-256, not ed25519 as the spec and plan
  claim. Design is unaffected; the prose needs fixing.
2026-07-24 14:53:26 +01:00
mrhid6 bc1cda26f8 docs: add implementation plans for licensing-core and instance-licensing
- 2026-07-24-licensing-core.md: 7 tasks. lk payload, offline verify, the
  trusted key slice, the noSign build tag, and lkctl for issuing by hand.
- 2026-07-24-instance-licensing.md: 10 tasks. Licence on the instance
  document, cached runtime state, deny-by-default mutation gate, feature
  gates, service-layer limits, settings UI, and migration 0005 to
  grandfather existing cloud instances.

Plan 2 opens by finishing the Org to Instance rename: 18 private identifiers
survived plan 0b's sweep. Nothing functional, but the file that gains the
licence cache is one of the two still carrying the old names.

Spec index updated with plan links and shipped status.
2026-07-24 14:48:45 +01:00
mrhid6 f646ce5c47 chore(server): remove rename-rollback
Migration 0004 is complete and verified on live, so the inverse rename has
served its purpose. Reverting the release now means restoring a backup.
2026-07-24 14:40:35 +01:00
mrhid6 1eb8c79623 Merge branch 'feat/shared-module'
Server Deploy / deploy (push) Successful in 2m43s
Shared Go module extraction (spec 0a) and the Org to Instance rename (spec 0b).

- shared/ module holds the documents and provisioning rules the control plane
  and sitesvc both write; sitesvc's hand-copied duplicates are deleted
- Org becomes Instance everywhere, including the org_id field on all 17
  tenant-scoped collections, via migration 0004_org_to_instance
- cmd/rename-rollback reverses the migration
- Go images now build from the repo root so the replace directive resolves

Migration verified against a seeded legacy fixture: lossless, tenant-isolated,
idempotent, resumable and reversible. NOT yet rehearsed against a production
snapshot, which plan 0b requires before deploying.
2026-07-24 14:21:56 +01:00
mrhid6 539403cccf fix(server): drop stale indexes before renaming the tenant key
Two defects found by running migration 0004 against a seeded legacy database.

A unique index on org_id treats a missing org_id as null. Renaming the field
strips it, so the second document collided and the whole update failed:

  E11000 duplicate key error collection: instance_oidc index: org_id_1
  dup key: { org_id: null }

The index cleanup therefore has to run BEFORE the field rename, not after.
rename-rollback needs the symmetric step for instance_id, or reverting hits
the same wall.

The detection also silently matched nothing: the driver decodes an index key
document as bson.D, not bson.M, so the type assertion always failed and no
index was ever dropped. IndexKeyedOn now handles both.
2026-07-24 14:15:05 +01:00
mrhid6 50a06dfdc0 refactor(site): rename Organisation to Instance
The signup form now posts instance_name, matching sitesvc.

Also restores site/next-env.d.ts. Its /// <reference> directives had been
stripped, which removed the Next.js type environment and failed the build
with "Cannot find name 'Promise'". Same cause as the agent's missing build
constraint.
2026-07-24 14:04:52 +01:00
mrhid6 b70ccc97d4 refactor(web): rename Organisation to Instance 2026-07-24 14:01:19 +01:00
mrhid6 3f0f12b111 refactor(sitesvc): rename Org to Instance, refuse an unmigrated database
The signup form's JSON field becomes instance_name, and the pending-signup
document field with it. That collection is sitesvc-private and expires after
24 hours, so no migration is needed, but in-flight signups written before the
deploy will fail verification.
2026-07-24 14:00:11 +01:00
mrhid6 3891a2c239 feat(server): add rename-rollback command for migration 0004 2026-07-24 13:59:04 +01:00
mrhid6 4f041d2f4b refactor(server): rename Org to Instance
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.
2026-07-24 13:58:41 +01:00
mrhid6 43a2fdb3a0 refactor(shared): rename Org to Instance 2026-07-24 13:54:31 +01:00
mrhid6 de0e7b7cae chore: commit go.work.sum 2026-07-24 13:53:35 +01:00
mrhid6 0226936758 fix(agent): restore the !linux build constraint on collect_other.go
Unrelated to the shared module work; pre-existing on main.

"_other" is not a GOOS suffix, so without the constraint this file compiled
on Linux alongside collect_linux.go and redeclared collect. Linux agent
builds failed, which agent-release.yml depends on.
2026-07-24 13:48:23 +01:00
mrhid6 42cd73c5b8 build: build Go images from the repo root for the shared module
Also pins the explicit shared require in both go.mod files. `go mod tidy`
in workspace mode drops it, which breaks the Docker build where no
workspace exists.
2026-07-24 13:45:45 +01:00
mrhid6 51622a922b refactor(sitesvc): use shared models and provision
Deletes internal/provision and the hand-mirrored Org and User structs. The
control plane and sitesvc now share one definition of both.
2026-07-24 13:43:58 +01:00
mrhid6 76d3111a72 refactor(server): use shared models, provision and indexes
Also removes the duplicate Slugify in stepscan.go; workflow step slugs now
use the shared definition too.
2026-07-24 13:42:44 +01:00
mrhid6 b335dc77e3 feat(shared): add EnsureCoreIndexes 2026-07-24 13:39:46 +01:00
mrhid6 51db5ab2e9 feat(shared): add CreateOrg, RollbackOrg and CreateUser
Adopts sitesvc's retry-on-duplicate-key slug loop. The control plane
previously returned an error when it lost the slug race.
2026-07-24 13:39:20 +01:00
mrhid6 d715d2150c feat(shared): add slug rules and shared document models 2026-07-24 13:38:52 +01:00
mrhid6 98687043f7 chore: scaffold shared module 2026-07-24 13:38:05 +01:00
mrhid6 bdc26e4799 docs: add implementation plans for specs 0a and 0b
- 2026-07-24-shared-module.md: 8 tasks, extract the shared Go module
- 2026-07-24-instance-rename.md: 9 tasks, Org -> Instance including the
  database field, with migration 0004 and a rollback command

No automated tests per instruction; verification is by compiler, grep, and
manual end-to-end plus production-snapshot rehearsal. Spec testing sections
updated to match.
2026-07-24 13:31:14 +01:00
mrhid6 b027ad3f7f docs: add licensing programme specs
Seven specs covering the licensing and billing programme:

- 0a shared-module: extract shared Go module, remove sitesvc duplication
- 0b instance-rename: Org -> Instance, including the database field
- 1  licensing-core: lk-signed licence payload, offline verify, CLI issuer
- 2  instance-licensing: storage, enforcement, degraded mode, settings UI
- 3  admin-backend: accounts, instances, licences, subscriptions, injection
- 4  admin-site: staff and customer portal
- 5  paddle-billing: catalog, checkout, webhooks, signup migration

Design only. No implementation.

Un-ignores docs/superpowers/ so specs are versioned.
2026-07-24 13:13:47 +01:00
mrhid6 fd32c96d86 feat: Updated site
Server Deploy / deploy (push) Successful in 2m23s
2026-07-24 11:50:11 +01:00
mrhid6 b74cd10dfc fix: Fixed bg zoffset
Server Deploy / deploy (push) Successful in 51s
2026-07-24 11:23:36 +01:00
mrhid6 460b4afb22 fix: FIxed compile error
Server Deploy / deploy (push) Successful in 1m45s
2026-07-24 11:18:14 +01:00
mrhid6 9cc9e68d90 fix: FIxed compile error
Server Deploy / deploy (push) Failing after 1m3s
2026-07-24 11:06:57 +01:00
mrhid6 accf7493e9 feat: Updated login bg
Server Deploy / deploy (push) Failing after 1m0s
2026-07-24 11:02:44 +01:00