Commit Graph
36 Commits
Author SHA1 Message Date
mrhid6 3f66370b1f feat: Add the backup dump
Collections are enumerated live rather than from a list, so a collection
added later is backed up with no code change. Documents are written as the
raw BSON the driver returned, so Decimal128, ObjectId, DateTime and binary
subtypes survive byte for byte.
2026-09-07 11:16:38 +00:00
mrhid6 525dc6af00 feat: Add the backup archive writer and reader
Open extracts and verifies every member against the manifest before the
reader is usable, so a corrupt archive is refused before a restore writes
its first document rather than halfway through.
2026-09-07 11:10:17 +00:00
mrhid6 07c68b170b feat: Add the backup archive manifest
KeyFingerprint is a pointer so an archive that recorded no key is a state
restore can report, not a default it silently treats as a match.
2026-09-07 11:08:02 +00:00
mrhid6 088ebe9848 feat: Add key fingerprinting for backup archives
Fingerprint hashes the raw key bytes rather than the hex string, so the
same key written in different cases fingerprints identically.
2026-09-07 11:03:48 +00:00
mrhid6 296bafd9e8 feat: Extract AES-GCM into shared/cryptobox
services/crypto.go keeps its function names and its KEY_ENCRYPTION_KEY
lookup and delegates the cipher, so vantagectl's verify probe can decrypt
with the same implementation rather than a second copy.
2026-09-07 10:55:54 +00:00
mrhid6 85d4c40ccb feat: status page schema, licence feature and indexes 2026-08-24 14:00:00 +00:00
mrhid6 72ed02d83b feat: Add a per-instance API token lifetime cap
A pointer with absent meaning no cap, so an upgrade allows never-expire
tokens exactly as before and an instance opts into the policy. It governs
issuance only: changing it never invalidates a token that already exists.
2026-08-12 14:22:12 +00:00
mrhid6 3320c87659 fix: Harden instance rename against interleaving and lost unwinds 2026-08-12 11:08:36 +00:00
mrhid6 f75b2024e0 feat: Add instance rename to shared provisioning 2026-08-12 10:01:56 +00:00
mrhid6 2e87d4c10d feat: Changes to self hosted purchase 2026-08-11 09:30:19 +01:00
mrhid6 6e8f384782 feat: vuln_scanning entitlement and documentation
Adds license.FeatureVulnScanning as the one name for the feature and a
catalogue row per deployment/tier, following console and oidc: features
are opt-in per customer, so no plan bundles it.

Documents the subsystem in CLAUDE.md, including that ScopedCollections is
the canonical registry instance deletion derives from — there is no
separate deletion list, which the plan had wrong.
2026-08-06 14:44:11 +01:00
mrhid6 18c2982acc feat: vulnerability scanning pipeline, matcher, scheduler and API
Completes tasks 10-15 and fixes what was outstanding:

- vulndb.Pull implemented with oras-go, streaming the ~50MB layer and
  staging both files before replacing either, so a failed pull leaves the
  previous database intact rather than a half-written one.
- db.go: Vulnerability.Severity is a string, not trivy Severity, so the
  int conversion did not compile. Severity now resolves vendor (highest
  when vendors disagree) then NVD then unknown, and CVSS is read too.
- findings.go: added sweepFixedFindings plus the fleet query, severity
  counts, rescan flag and accept/unaccept the API needs.
- vulnrules.go: added rule CRUD and the digest builder. ResolveTargets
  returns []models.Server, not []string, so filterByServers was wrong.
- api/vulnerabilities.go was an empty file while handlers.go registered
  twelve routes against it; written, grouped by CVE.
- shared/mail: added the missing sender. The templates were orphaned and
  the HTML one was a copy of the text one, defining "subject" (which
  html/template would escape) and emitting no markup. render.go parses
  every template in init(), so a bad one panics server, admin and sitesvc
  at boot — go build never runs init(), which is why nothing complained.
- notify: digests dispatch through their own path so SMTP gets the digest
  template rather than arriving dressed as a monitor alert.
2026-08-06 14:33:46 +01:00
mrhid6 fad7621d42 feat: Removed email alert settings 2026-08-03 14:40:15 +01:00
mrhid6 9d6c530b5c feat: add local_login_enabled setting with absent-means-on default 2026-08-03 10:36:48 +01:00
mrhid6 b8b553c695 feat: Removed unused test units 2026-08-03 10:18:01 +01:00
mrhid6 4b53970de3 feat: Updated package path to match repo 2026-07-28 10:01:40 +01:00
mrhid6 dc0d6cb2d2 feat: Move mail system to shared 2026-07-28 09:50:46 +01:00
mrhid6 03cf1715b2 feat: Updated purchase page 2026-07-27 14:59:43 +01:00
mrhid6andClaude Opus 5 ae503c2d3b feat(license): two deployments times three tiers, and two new limits
Plans are keyed on (deployment, tier) rather than tier alone, which is what
ends Free being cloud-only by construction - there is a self-hosted Free
plan now, so the one-per-account rule has to be enforced per deployment
instead of falling out of the plan table.

tier self_hosted becomes a legacy value no new licence carries.
NormaliseTier maps it to self-hosted Professional, which is what it always
granted, so blobs we cannot re-sign keep working.

Limits.FillUnset exists because a licence signed before a field existed
decodes it as 0, and 0 would read as no monitors and an audit log trimmed
to nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:41:43 +01:00
mrhid6andClaude Opus 5 df603c7219 fix(admin): licence and plan features serialise as [], never null
Free has no features, so a nil Go slice served "features": null. The
portal's type said string[], called .length on it, and took the page down.

The guarantee lives on a named Features type with its own MarshalJSON
rather than at each of the six places a licence or plan is serialised,
because the seventh is the one that would have been forgotten. Applying it
at marshal time also fixes rows already holding null in Mongo, which a
write-side fix alone would not.

Same class, found while checking the rest: staffListInstances built its
expiring filter from a nil []string, marshalling to $in: null, which Mongo
rejects — so the quiet week when nothing is expiring is precisely when the
staff Operations page's own query would have failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 18:10:07 +01:00
mrhid6andClaude Opus 5 ec5808be25 feat(shared): auth_source constants and hq_user_id on User
Nothing writes them yet. They land now so phases 2 and 3 do not require a
second rebuild of every service that consumes the shared module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:52:15 +01:00
mrhid6andClaude Opus 5 af5043c353 feat(shared): unique users index is (instance_id, email)
One address is one user within an instance, not globally, so an account's
people can be projected into every instance they are granted.

The replacement index is created before email_1 is dropped, so a failure
at any point leaves a working constraint. The drop is idempotent and
tolerates two services racing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:48:27 +01:00
mrhid6andClaude Opus 5 a0ebe85a31 chore: declare go 1.26 rather than 1.26.4 across the workspace
The 1.26.4 floor meant a base toolchain of 1.26.2 could not load the
workspace at all: the terminal worked only because GOTOOLCHAIN=auto
silently swapped in a downloaded 1.26.4, and gopls -- which does not get
that switch -- failed every packages.Load with

  go: go.work requires go >= 1.26.4 (running go 1.26.2)

Nothing needed the patch-level floor. agent/go.mod already declared plain
go 1.26, so this makes the workspace uniform rather than introducing a new
convention, and CI is unaffected because golang:1.26 is already newer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 19:33:58 +01:00
mrhid6 d7ada3eb76 fix(server): add the lk go.sum entry the workspace was masking
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 33021e072b feat(shared): add licence fields to Instance 2026-07-24 15:08:48 +01:00
mrhid6 fbd0b17ca3 feat(license): trust the production signing key 2026-07-24 15:01:03 +01:00
mrhid6 a0502558fd feat(license): add lkctl for issuing licences by hand 2026-07-24 14:59:13 +01:00
mrhid6 b26472dfe8 feat(license): add offline verification 2026-07-24 14:58:25 +01:00
mrhid6 5b25797d77 feat(license): add signing and the trusted key list 2026-07-24 14:57:52 +01:00
mrhid6 b1a932b765 feat(license): add the licence payload and tier seed table 2026-07-24 14:57:21 +01:00
mrhid6 788d98b0c1 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 35b7bc1a63 refactor(shared): rename Org to Instance 2026-07-24 13:54:31 +01:00
mrhid6 59b0e63c2b feat(shared): add EnsureCoreIndexes 2026-07-24 13:39:46 +01:00
mrhid6 06cc878d2e 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 ea460d7408 feat(shared): add slug rules and shared document models 2026-07-24 13:38:52 +01:00
mrhid6 4da2e546b7 chore: scaffold shared module 2026-07-24 13:38:05 +01:00