62 Commits
Author SHA1 Message Date
mrhid6 3afc4ab012 docs: workload registry plan; remove tests from both plans
Both plans now verify by build, vet and manual checks written into the
tasks. Spec verification sections updated to match so they no longer
describe tests that will not be written.
2026-08-06 11:27:37 +01:00
mrhid6 d1ac3e98ce docs: design for the workload registry
Agents enumerate Docker containers, compose stacks and systemd services;
start/stop/restart and bounded log snapshots from the UI.

Sub-project B, Linux only. Live log following stays in the console.
2026-08-06 11:11:26 +01:00
mrhid6 fe7bc300e2 docs: implementation plan for package inventory and CVE findings
17 tasks, TDD where the logic is pure. Corrects two spec claims:
the server reads features via License.HasFeature rather than admin's
entitlement directly, and shared/mail/render_test.go does not exist.
2026-08-06 10:49:10 +01:00
mrhid6 00c03c365d docs: design for package inventory and CVE findings
Agents report installed packages; the control plane matches them against
trivy-db and raises findings that link to the existing ApplyUpdatesCmd
patching path.

Scoped to sub-project A, Linux only. Container registry, image scanning
and compliance baselines are separate specs.
2026-08-06 10:33:54 +01:00
mrhid6 3530ce6cb7 docs: implementation plans for server tags and scheduled workflows 2026-08-04 13:26:04 +01:00
mrhid6 09522c2566 docs: design for server tags and scheduled workflows 2026-08-04 13:13:12 +01:00
mrhid6 c56bfb7270 docs: implementation plan for multiple auth providers 2026-08-03 10:23:35 +01:00
mrhid6 1e2132c1a1 docs: Cleanup old specs and plans 2026-08-03 10:15:54 +01:00
mrhid6 19ef773690 docs: drop legacy OIDC callback from multi-provider design 2026-08-03 10:13:26 +01:00
mrhid6 c5aae0614a docs: design for multiple auth providers 2026-08-03 10:09:13 +01:00
mrhid6 d3d8dba3ff docs: Implementation plan for agent-relayed console proxy 2026-07-29 12:26:16 +01:00
mrhid6 6d047e25ab docs: Design for agent-relayed console proxy 2026-07-29 12:16:45 +01:00
mrhid6 bc6c7cdb5a feat: Updated docker file
Server Deploy / deploy (push) Successful in 2m35s
2026-07-28 16:04:51 +01:00
mrhid6 d9945882e5 docs: serve the documentation site under /docs on the marketing host 2026-07-28 15:22:20 +01:00
mrhid6 9db16522e3 docs: design for the documentation site 2026-07-28 15:20:07 +01:00
mrhid6 dabe6fe3aa feat: Updated package path to match repo
Agent Release / build (push) Failing after 25s
Agent Release / msi (push) Skipped
Server Deploy / deploy (push) Successful in 2m47s
2026-07-28 10:01:40 +01:00
mrhid6 effd991c31 docs: mobile responsive design spec and implementation plan for web/ 2026-07-27 22:24:26 +01:00
mrhid6 0a86167c44 docs: updated docs
Server Deploy / deploy (push) Successful in 1m56s
2026-07-27 10:57:19 +01:00
mrhid6 3537ec59dc docs+deploy: Paddle env, webhook route, collections, CI build args, and spec status 2026-07-27 10:55:41 +01:00
mrhid6andClaude Opus 5 c4e6ad5485 feat(license): metered licensing — catalogue, entitlements, and enforcement
Server Deploy / deploy (push) Successful in 5m22s
Implements spec 7 tasks 2-10 on top of the six-plan payload from task 1.

Admin: plans re-keyed on (deployment, tier); new catalogue collection holds
every Paddle price ID (one row per priceable component); new entitlements
collection holds desired beside granted. admin/internal/catalogue owns both
folds — entitlement to licence limits, and entitlement to Paddle line items —
so the base allowance is subtracted in exactly one place. licensing.Issue now
snapshots the instance's granted entitlement, never desired. Free is enforced
per account AND deployment. Staff endpoints for plans, catalogue and
entitlements; Free self-hosted can be claimed and renewed on its annual term;
the reaper stays cloud-only.

Server: enforces the monitor cap, audit-log retention (daily sweep, skips
Unlimited and lapsed instances), and gates the OIDC callback. Unset limits are
filled from the seed plan at the single decode site so old blobs never read as
zero.

Frontends: adminsite gains a catalogue price-ID editor, six-plan allowance
screen, and a catalogue-driven PlanConfigurator mounted on the staff instance
page. web shows monitors, audit retention and support level on the licence page.

Docs: CLAUDE.md, spec index and plan 5 preamble updated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 09:37:39 +01:00
mrhid6andClaude Opus 5 73b6b548f0 docs: implementation plan for spec 7, metered licensing
Ten tasks, each ending in something independently verifiable. The plan
stops at the Paddle boundary on purpose: spec 7 lands before plan 5, so
there is no client, no webhook and no checkout here, and the configurator
is built as a component mounted for staff first rather than as a customer
screen with nothing behind it.

Corrects two things the spec got wrong about the control plane. Feature
gating is already built and mounted, so Free tenants have already lost the
console and no customer email is owed. The only real gap is that
HandleOIDCCallback lacks the check HandleOIDCStart already has, which is
the half that completes a sign-in.

Adds the Free self-hosted lifecycle the spec called for and the first draft
of the plan missed: linking issues nothing today, and renewInstance
hardcodes a monthly term that would hand a self-hosted install a one-month
licence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:34:34 +01:00
mrhid6andClaude Opus 5 ff0caf5a90 docs: spec 7, metered licensing and the two-by-three plan matrix
Two deployments times three tiers, servers metered per month, console and
SSO opted into individually. plans is re-keyed on (deployment, tier); every
Paddle price ID moves out of plans into a new catalogue collection; a new
entitlements collection holds desired beside granted, and a licence is only
ever signed from granted.

Spec 5's plan is revised rather than followed: it assumes one price per
subscription and a metered plan has three or more. Nothing of it has
shipped, so the revision costs a rewrite of an unstarted plan.

Free stops being cloud-only by construction, which means the plan/instance
deployment comparison in licensing.Issue no longer enforces it and
checkFreeLimit has to count per deployment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:16:35 +01:00
mrhid6 87dc9fc858 inital paddle billing docs 2026-07-26 22:57:57 +01:00
mrhid6 fbad3e44e5 doc: Added phase 3 plan
Server Deploy / deploy (push) Successful in 1m37s
2026-07-26 15:42:07 +01:00
mrhid6andClaude Opus 5 78a0a610be docs: phase 2 plan, creation and Free lifecycle
Ten tasks: cloudprov, instance creation, renewal, the notice sweep, the
reaper, the reconciler tie-in, the HQ and marketing UI, the sitesvc
cutover, and an end-to-end verification.

The reaper sweeps once at boot as inject's reconciler does, which is what
makes it observable at all — the ticker is hourly and deletion is
measured in days.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 13:12:29 +01:00
mrhid6andClaude Opus 5 88f49a96ae docs: phase 1 plan, identity
Seven tasks: the index swap, the hq fields, the three scoped lookups,
removing admin's unscoped control-plane login, and an end-to-end
verification that two users sharing one address sign in to different
instances.

Also corrects the spec's phase list, which claimed phase 1 projects the
creator as owner. Projection needs instance creation, which is phase 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:28:25 +01:00
mrhid6andClaude Opus 5 d15ab78bd5 docs: spec 6 revised for account membership
An account has people, and those people belong to several cloud
instances. That requires dropping the global unique index on users.email
for a compound (instance_id, email), scoping the two unscoped lookups
that made the global index load-bearing, and projecting HQ users into
instances as real control-plane users.

Self-hosted instances are never projected into; their users stay local.

Restructured into three phases: identity, creation and lifecycle,
membership.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:16:38 +01:00
mrhid6andClaude Opus 5 8eb14c1502 docs: spec 6, cloud instance creation
Separates having an account from having an instance. Signup creates only
the HQ account; the customer then creates a Free cloud instance from the
portal, which provisions the control-plane instance, the admin_instances
row and a Free licence in one path.

Supersedes spec 5's signup-migration section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:03:14 +01:00
mrhid6andClaude Opus 5 85f6d47024 feat(adminsite): image, compose service, image build and docs
Publishes adminsite on 3004 -- 3000 is web, 3003 is the marketing site since
the port shuffle -- and adds the sixth CI image.

CLAUDE.md gains both new services. admin/ was never documented there at all:
the backend plan's wiring task covered compose and CI but not the file every
session actually loads. So this records admin's whole REST surface, its
404-not-403 rule, the three visual identities and, most importantly, that
adminsite/ and site/ share one token set with nothing enforcing the match --
the same hazard shape as sitesvc's mirrored slug rules.

Also notes that admin's REDIS_ADDR reaches admin only, because the base
compose hardcodes redis:6379 for server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 21:24:37 +01:00
mrhid6andClaude Opus 5 24060b2c5a chore(adminsite): remove the frontend test suite
Removes vitest, React Testing Library, the config, the setup file and all
eleven test files, plus the test scripts and dev dependencies. Done at the
user's direction; it matches the rest of the repo, which has no automated
tests in any language.

All eleven were observed passing before removal, and their assertions are
kept in the plan as acceptance criteria to check by hand rather than deleted
outright -- they are the clearest statement of what each component has to do.

Consequence worth stating: Task 16's manual pass is now the only verification
that exists for spec 4. Four behaviours it must cover carefully, because each
is easy to break invisibly: 404-not-403 scoping, the expired card naming what
still works, relink disabling at zero, and the blob fallback when a download
fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 21:19:34 +01:00
mrhid6andClaude Opus 5 55526263a0 feat(admin): staff instance detail, subscriptions and richer search
Closes the rest of what spec 4's screens need. Account search now also
matches a Paddle customer ID and resolves an instance UUID to its owning
account -- a support email often contains a UUID and nothing else, and the
old search returned nothing for it.

GET /api/staff/instances/:id is the "why did this stop working" screen's
data: the instance, its account, its whole licence history newest first, and
whether the control plane currently holds the blob we think it holds.
Injection state is reported only for cloud, because for self-hosted the
customer holds the blob and there is nothing for us to have written.

Account detail gains subscriptions, customer users and its own audit trail.
No secret leaves: the password hash and both verify-token fields are json:"-".

The control-plane write surface is unchanged -- still exactly one UpdateOne
of three licence fields in inject.go, with reads everywhere else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:50:11 +01:00
mrhid6andClaude Opus 5 4bb7400b8e feat(admin): session probe, self-hosted signup and the relink cap
Adds GET /auth/me so the admin site's route guards can know who is signed
in, POST /auth/signup for self-hosted customers, and max_relinks on the
account payload so the UI never hardcodes a rule the backend enforces.

Signup follows sitesvc's proven shape: honeypot answered as success, a
generic 201 when the address already exists, and nothing usable until the
emailed link is opened.

Also fixes a lockout found while verifying it. When the verification email
failed, the account was rolled back but the customer_users row survived --
an orphan that can never be signed in to and that holds the unique index on
email, so every later signup with that address got a cheerful 201 and the
customer was locked out of their own address with no visible error.
CreateCustomerUser now undoes its own insert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:48:09 +01:00
mrhid6andClaude Opus 5 79afcc2e16 docs(plan): match the admin site to site/'s design tokens
Replaces the petrol-and-serif direction with site/app/globals.css's token
set copied verbatim: brand navy accent, the same neutrals, the same clamp
type scale, 1200px rail, 4px radii, and site/'s heading treatment of the
sans at weight 800 rather than a serif display face.

The semantic three are aliased rather than renamed -- globals.css carries
site/'s --up/--pend/--down, and Tailwind exposes them as valid/warn/expired
so each app names them for what it actually shows. Same colours either way.

Tailwind now holds var() references only, so no component or config can
carry a hex value and drift. Buttons match site/'s .btn--solid and
.btn--line, including the neutral border on the secondary variant.

Records the caveat this creates: site/'s dark accent (#5b9be8) sits nearer
web/'s indigo than the light navy does, so the "which app am I in" cue rests
on the light ground and dark must not become the default.

Drops the guilloche ornament -- site/ carries nothing like it. The ledger
stays, because it is information design rather than decoration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:31:55 +01:00
mrhid6andClaude Opus 5 8708bd9498 docs(plan): add the admin site implementation plan
Sixteen tasks: two that close gaps in admin's API, thirteen frontend, one
verification pass.

Auditing spec 4's screens against what spec 3 actually shipped turned up
eight things the UI needs and the backend does not expose -- including no
GET /auth/me at all, which no route guard can work without, and no signup
endpoint for the self-hosted flow the spec's app/signup/ implies. Those are
tasks 1 and 2 rather than frontend improvisation.

Records the approved design direction as fixed constraints: light ground
because web/ is dark-locked and telling the two apart is what stops a
Reissue landing in the wrong tab, petrol accent because green, amber and red
are spoken for by licence state and indigo belongs to web/, and the licence
ledger as the one screen that earns ornament.

Serves vantage-hq.hostxtra.co.uk on 3004 -- 3002 is the marketing site now,
and the host stays outside *.vantage.hostxtra.co.uk because that namespace
is per-tenant instance subdomains.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:22:00 +01:00
mrhid6andClaude Opus 5 cfdc00552e docs: mark the admin backend shipped and verified
Ticks all 66 plan steps and records spec 3 as shipped.

Also corrects a stale line that still claimed existing cloud tenants are
grandfathered by migration 0005. That migration was reverted before plan 2
shipped; those instances are read-only until licensed by hand, and the same
file already said so one table above.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 19:21:45 +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 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 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 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 97bc766afb feat: Remove plans 2026-07-22 13:03:19 +01:00
mrhid6 0d4fb896bb docs(spec): correct collection names and per-org caveats
The spec named the audit collection "audit" and the channels collection
"channels"; the code uses audit_logs and notification_channels. The
migration followed the spec, which is how it came to backfill two
collections that do not exist.
2026-07-22 11:13:19 +01:00
mrhid6 ea73fc3a18 docs(plan): saas auth + orgs implementation plan 2026-07-21 16:20:20 +01:00
mrhid6 75b86e3843 docs(spec): add org slug, host-based resolution, single gRPC endpoint 2026-07-21 16:15:29 +01:00
mrhid6 9767e18123 chore: removed old plans 2026-07-21 15:54:11 +01:00
mrhid6 a28157dcf8 feat(server): store inventory and handle ReportInventory RPC
Server Deploy / deploy (push) Successful in 1m4s
2026-07-21 14:05:19 +01:00
mrhid6 fb1a1292ec docs: add service monitoring phases to fleet inventory plan
Server Deploy / deploy (push) Successful in 21s
2026-07-21 14:01:50 +01:00
mrhid6 8398fd2279 docs: implementation plan for adhoc steps, import/export, defaults, auto-outputs 2026-07-21 10:11:16 +01:00