Commit Graph

2002 Commits

Author SHA1 Message Date
Julian Sommer 804c1e822b fix: enforce resource-scoped IAM policies (#871) 2026-08-25 09:43:20 +10:00
Josh Siviour 8692871d15 refactor: convert the RDS reconciler to the shared kvlease package (#870) 2026-08-25 09:30:58 +10:00
Josh Siviour 578566b801 refactor: convert the bedrock reaper to the shared kvlease package (#869) 2026-08-25 08:58:02 +10:00
Josh Siviour 5675a2b37c refactor: convert the ECS scheduler to the shared kvlease package (#867)
* refactor: ECS scheduler to used shared kvlease package

* fix: wait for the ECS lease release before Run returns
2026-08-25 08:05:17 +10:00
Julian Sommer 4869f3b021 fix: evaluate EC2 policies against the request's resource ARNs (#866) 2026-08-24 15:49:07 +10:00
Josh Siviour 4bbc96f1e0 refactor: extract the KV leader lease into one package (#865) 2026-08-24 14:38:54 +10:00
Julian Sommer 6fc7bdfab8 refactor: share EC2 ARN construction in a new arn leaf package
getResourceType and ec2ARN both encoded the same resource-type knowledge on
opposite sides of the gateway. The unknown sentinel becomes ok == false, since a
sentinel that still looks like a valid ARN segment is how a wrong ARN gets built;
DescribeTags keeps emitting "unknown" on the wire.
2026-08-24 14:36:17 +10:00
Julian Sommer 82654ae5e4 chore: enable prefer-destructing in test files 2026-08-24 14:29:59 +10:00
Julian Sommer 0c6606247a chore: enable no-misused-promises 2026-08-24 14:23:09 +10:00
Julian Sommer 36d17c6a95 chore: enable jsx-a11y/prefer-tag-over-role 2026-08-24 14:15:31 +10:00
Julian Sommer d80542ac80 fix: pin predastore dev version 2026-08-24 14:14:20 +10:00
Julian Sommer f58fc9c00a chore: enable no-confusing-void-expression 2026-08-24 14:07:35 +10:00
Julian Sommer 58e1bd2c5a fix: reject and enforce IAM Condition blocks on identity policies (#864) 2026-08-24 14:04:32 +10:00
Julian Sommer 0c19132949 fix: enable unicorn/prefer-single-call 2026-08-24 12:34:36 +10:00
Julian Sommer 71b94cd3c2 chore: enable more vitest lint rules 2026-08-24 12:31:51 +10:00
Julian Sommer c8aafa5de8 chore: delete stale migrations since we forced clean re init anyway 2026-08-24 12:02:21 +10:00
Julian Sommer 4fbde4daa8 chore: fix test 2026-08-24 11:52:04 +10:00
Julian Sommer 32f2d2f7f6 feat(ui): enable ultracite's anti-slop oxlint preset (#862) 2026-08-24 11:24:13 +10:00
Josh Siviour f0ccf0d0c3 Fix/ecs scheduler leader cas (#861)
* fix(ecs): guard scheduler leader refresh and release with a revision CAS

* Ignore .cache

* update comment
2026-08-24 11:15:58 +10:00
Julian Sommer 52ae70ca33 refactor(ui): type the form values the CLI panels read (#860) 2026-08-24 10:59:33 +10:00
Julian Sommer ae112a5a49 test(ui): type the query and SDK mock test helpers (#859) 2026-08-24 10:58:32 +10:00
Julian Sommer dc92be8b7e refactor(ui): validate route search params with zod schemas (#858) 2026-08-24 10:55:22 +10:00
Julian Sommer 461cdc5593 fix: improve tests 2026-08-24 10:11:32 +10:00
Julian Sommer 4122ef6b56 chore: delete duplicate tests 2026-08-24 09:59:06 +10:00
Julian Sommer da0c5cf27f chore: delete useless mocks 2026-08-24 09:52:16 +10:00
Julian Sommer 56d8792fa0 fix: more test cleanup 2026-08-24 09:47:07 +10:00
Julian Sommer 8e59b6c34e chore: remove useless tests 2026-08-24 09:43:30 +10:00
dependabot[bot] 97ed54445a chore: bump the npm-dependencies group across 1 directory with 23 updates (#857) 2026-08-24 09:29:59 +10:00
Julian Sommer 2d925190dc fix: disable react compiler in tests 2026-08-24 09:26:41 +10:00
Julian Sommer 1d7f2f066a fix: disable now unneeded lint rules 2026-08-24 09:19:58 +10:00
Julian Sommer 3eb3ffdc9a feat: use oxlint native react compiler 2026-08-24 09:18:05 +10:00
dependabot[bot] 9b5acc738c chore: bump the go-dependencies group with 8 updates (#855) 2026-08-24 09:03:18 +10:00
Josh Siviour a80ffbc0a0 fix: suppress OVN DHCP on a statically-addressed customer ENI port (#854)
The RDS customer endpoint ENI is configured statically in the guest, but OVN still attached dhcpv4_options to its logical switch port. The guest's cloud-init dhcpcd then took an OVN DHCP lease on that NIC and flushed the static address at the 3600s lease expiry, roughly an hour after every boot. Thread a SuppressDHCP flag from the customer-ENI creation through the ENI record, port event, and PortSpec, and gate the dhcpv4_options assignment at both create sites on it. The primary NIC keeps DHCP for IMDS; a delete and recreate never re-adds DHCP because the reconciler intent carries the flag too.
2026-08-23 23:18:13 +10:00
Ben Duncan 8b3c706ea3 docs: rework the multi-node install guide, fix cross-doc links
Multi-node install
- Lead with the fact that the guide builds a three-node cluster, and say
  how to go beyond three. The callout sits inside ## Overview because the
  docs site discards everything above the first section heading.
- Replace the OVN-only sizing prose with a table covering all three
  distributed layers, and move the OVN control-plane detail into the VPC
  networking guide, linked from here.
- Add minimum and recommended hardware.
- Move the object storage layout to the end as background; it was the
  second thing a reader hit.
- Collapse the ISO conversion notes from a section into one skippable
  box, now that --force is unconditional in Step 4 and the formation
  port needs no manual intervention.
- Expand verification with spx get nodes and spx top nodes, including
  what to look for in each.
- Drop the install-node.sh shortcut (internal tooling) and the
  "adding a node later" section (no good story yet).

Corrections found while writing
- Servers 4+ were described as running no database. They run predastore
  and viperblock like every other node; only the OVN database cluster is
  capped at three. Fixed in four places.
- Step 4 never needed the firewall down: spx admin init opens the
  formation port itself for the formation window. Only Step 3's
  peer-scoped OVN ports are affected.
- `nft list table inet spinifex_filter | grep spinifex_peers` could never
  match — the peer list is an nft define, expanded at load time. Anyone
  following it would have seen empty output and assumed the worst.
- Disambiguate "database": with predastore, NATS and OVN all in play it
  needed qualifying throughout.
- Align the example output region with the AWS_REGION the guide exports.

Cross-doc links
- Rewrite relative and category-prefixed links to the canonical
  /docs/<slug> form. The site publishes docs flat, so ../foo/README.md
  and /docs/category/foo both 404.
- Point the six links to unpublished docs at the source repo instead.
- Fix four fragments written against GitHub's anchor algorithm.
2026-08-21 14:14:28 -05:00
Ben Duncan b834910e64 UCS tweaks 2026-08-21 09:53:04 -05:00
Josh Siviour b482b41e96 feat: implement EC2 MonitorInstances and UnmonitorInstances (#853)
* feat: implement EC2 MonitorInstances and UnmonitorInstances

The telemetry collector already supports both EC2 monitoring tiers and
already switches a running poller between them when its discovery file
changes, but nothing could make that change: Monitoring.Enabled was only
ever written at launch, and both API actions returned InvalidAction.

Route through the owning daemon over ec2.cmd.<id> rather than the
spinifex-workers queue group, so an instance running on any node is
reachable; no responders falls back to the shared stopped store, where the
write is a CAS so a racing start-claim cannot resurrect a deleted record.

Also report Monitoring on DescribeInstances. It is derived in
ProjectInstance from the same field the collector's period reads, so a
describe cannot disagree with the tier actually being collected and
instances launched before this change report disabled without a backfill.

* fix: wake the telemetry poller when its monitoring tier changes

poller.run re-read its period inside the tick loop, so a reset waited out
the interval being replaced: moving a 300s instance to the detailed tier
took up to five minutes to have any effect, which reads as the API call
not working. Found verifying monitor-instances on env13.

updateMeta now signals a buffered retier channel when PeriodSeconds moves
and run selects on it alongside the ticker. The signal only resets the
ticker rather than forcing a tick, so a published delta still spans the
period its batch declares. The send is non-blocking and the channel
coalesces, so a slow poller cannot stall the reconcile loop.
2026-08-21 15:36:40 +10:00
Josh Siviour bed994859c fix: durable Ochre/RDS appliance lifecycle (static ENI, teardown GC, stale LSP) (#852)
* fix: join the daemon host-port ENI to the appliance security group

The customer ENI's self-referencing SG drops all ingress except its own
members, so the daemon's host-port ENI could open TCP but never complete a
postgres handshake. Carry the appliance's security groups onto the daemon ENI
at create, and re-associate an already-created one in place.

* fix: complete teardown GC when a volume's metadata doc is already gone

DeleteVolumeOnTerminate and DetachVolumeOnTerminate propagated the
NoSuchKey error from a missing volume metadata doc as a hard failure, so
the teardown reaper backed off forever and never purged the terminated
record. A missing metadata doc means the volume is already gone, so treat
it as success via objectstore.IsNoSuchKeyError at both call sites, with a
matching tolerance in the instance-cleaner adapter.

* fix: statically address the RDS customer ENI so it survives the DHCP lease

The customer ENI was configured via a one-shot udhcpc with no renewal
against OVN's 3600s lease, so the DB endpoint went address-less an hour
after every boot. Resolve the customer subnet's prefix and gateway at
launch and emit the ENI as a static NIC (CIDR + gateway, never the
default route), mirroring the mgmt NIC, so it no longer depends on the
lease being renewed. The guest static branch now installs the same
source-based return-path policy a non-default DHCP NIC gets.

* fix: delete the OVN logical switch port when an ENI is deleted

An ENI delete published vpc.delete-port fire-and-forget, so a lost or slow
message let the ENI record and its IP free through with the logical switch
port still in OVN. A recreate onto the same IP then had two ports carrying
it and the router latched the stale MAC, blackholing the datapath. Publish
the delete synchronously so the port is confirmed gone before reuse, and
make DeletePort idempotent so an already-absent port is a no-op.
2026-08-21 15:11:02 +10:00
tomnewton-mulga e22db02d24 fix: Rewrite Cisco and OnLogic docs 2026-08-21 14:06:00 +10:00
Josh Siviour 3bb35deea2 fix: dial the ochre appliance customer ENI, not the management NIC (#851)
Both the endpoint ENI and the management NIC carry the rds-db-instance tag,
so a first-match pick non-deterministically dialed the unreachable management
NIC. Select the endpoint ENI by its RDS description, falling back to any usable
tagged NIC.
2026-08-21 14:03:59 +10:00
Josh Siviour 7bfeaf885c fix: give LB microVM initramfs headroom by bumping sys.micro to 256 MiB (#850)
The ELBv2 LB microVM boots with -m 128 (sys.micro). After kernel
reservations the 37.7 MiB initramfs — 45% of it the Go 1.27 lb-agent —
leaves under 512 KiB of headroom, so the guest panics with 'Initramfs
unpacking failed' before init and lb-agent never heartbeats.

Bump sys.micro to 256 MiB so the initramfs unpacks with margin, and add
an uncompressed-initramfs gate to build-microvm-image.sh (the compressed
gate was blind to the guest-RAM footprint) so a future bump fails the
build loudly instead of panicking in production.
2026-08-20 23:14:20 +10:00
Josh Siviour 3e4148cf15 fix: let a class-change modify recover a failed RDS instance whose agent is dead (#849)
A class-change ModifyDBInstance is the intended remedy for a failed DB
instance, but applyPendingModifications applied the parameter group through the
live instance agent first. A dead agent returned ErrCommandUnreachable and
aborted the modify before replaceInstanceVM could run, so the one recovery
lever the API offers could not run for the case that needs it most.

The replacement VM's fresh agent applies the resolved set itself on boot, so
the pre-replace apply against the old dead agent is redundant for a class
change. Tolerate ErrCommandUnreachable there: resolve the class-correct set
(a KV read, no agent), persist it for the fresh agent, and skip only the dead
agent's apply. Parameter-group-only modifies still fail on a dead agent.
2026-08-20 23:10:53 +10:00
Josh Siviour c941830664 feat: gate self-host inference behind capacity-based ThrottlingException (#848)
* feat: gate self-host inference behind capacity-based ThrottlingException

Add a process-scoped concurrency limiter keyed by (servingAccountID,
modelID) with capacity = catalog MaxConcurrency x max(ModelUnits, 1),
and apply it to every self-host Converse/ConverseStream/InvokeModel/
InvokeModelWithResponseStream path. Over-capacity requests reject
immediately with ThrottlingException instead of queuing inside vLLM,
matching real Bedrock. Managed (Anthropic) providers are untouched.

* test: mark admission_test as in-package for the unexported limiter

* test: cover capacity admission gate and throttling
2026-08-20 22:28:52 +10:00
Josh Siviour c918f6834a feat: back up the ochre pgvector schema to object storage (#847)
* feat: back up the ochre pgvector schema to object storage

Adds account-scoped pg_dump/restore of the per-account kb_<accountID>
schema to predastore, gzip-compressed and keyed under the account's
own prefix. Backup shells out to pg_dump (--no-owner --no-privileges
--clean --if-exists) behind a PgDumper seam; restore runs psql then
repairs the account role's grants via EnsureAccount/RegrantAccount
since the dump carries no ownership. Exposed as
`spx admin ochre vector backup/restore --account <id>`.

* test: cover ochre vector backup dump/restore and error paths
2026-08-20 22:28:25 +10:00
Julian Sommer f10c9434d1 fix: detect KV CAS conflicts on replicated buckets (#843) 2026-08-20 16:16:22 +10:00
Josh Siviour f9c84919ef feat: co-served GPU model bundles (vLLM + TEI) with family-aware reaper (#845)
* feat: co-served GPU model bundles (catalog, lifecycle, embed/rerank routing)

Serve the generative LLM, embedder and reranker as one co-resident GPU
bundle: EMBEDDING/RERANK catalog modalities + co-serve group, bundle-aware
admission (one GPU claim sized to summed member VRAM), single-flight one-VM
launch with a member->port EndpointRecord, DynamicEndpointResolver member
routing. Ochrevector embed/rerank/token-introspection resolve through the
resolver (static single-endpoint map retired); rerank degrades to plain KNN on
absent/failure. Public InvokeModel-embeddings and top-level Rerank deferred.

* feat: co-serve vLLM+TEI bundle image with per-engine readiness

Replace the single-model vllm-serve unit with a generic bedrock-serve@
systemd template driven by per-member BEDROCK_* env files, dispatching to
vLLM or text-embeddings-router by engine and binding each member to its own
port and weights device. TEI serves directly on its serve port; readiness
probes each member on the route its engine answers (vLLM /v1/models, TEI
/health) with the engine family carried end-to-end from the catalog.

* fix: serve nomic embedder correctly in the co-serve bundle

Bound embeddings requests to the TEI client batch limit so a large
document's chunks are split across requests instead of 422ing. Serve
nomic with explicit mean pooling, since the flat weights layout omits
1_Pooling/config.json and TEI would otherwise default to CLS. Add
bge-base-en-v1.5 as a standalone alternative embedder.

* fix: make the bedrock reaper family-aware and pin-safe

A pinned endpoint is now exempt from terminate-and-relaunch on scrape
failure, mirroring how it is already exempt from idle reclaim, so a
transient unreachability can no longer tear down operator- or
commitment-managed serving.

A bundle with no generative vLLM member exposes no vLLM load metrics, so
it is liveness-probed on /health and kept warm rather than scraped for
idleness on a route it does not serve and reaped in a loop.

* test: lock in co-serve group-member convergence to one VM

* fix: pin self-host HF repo/revision in catalog and stop nested config flattening

Carry HFRepo/HFRevision per self-host catalog entry; weights pull defaults
--hf-repo and --revision from them so a bare pull cannot silently grab a
regressed upstream main (the nomic v5 config.json duplicate-field break).
Restrict fixed config filenames to the repo root so a nested config.json
(sentence-transformers 1_Pooling/) is dropped instead of flattening onto the
model config, and refuse any two source paths that collide at one dest key.

* fix: let endpoint delete clear an account-scoped pinned record and report honestly

A bare 'endpoint delete' resolved the Global key, which never held a pinned,
account-scoped record, then printed 'torn down' over the idempotent no-op while
the record survived. Add --account to the delete command so an operator can
target the account 'endpoint list' shows, and return DeleteEndpointOutput.Removed
so the CLI names a real teardown versus a lookup that found nothing and points
at --account. A gone VM already tolerates NotFound, so a scoped delete clears a
record whose instance was terminated out of band.
2026-08-20 16:08:55 +10:00
Josh Siviour 8eb3e4f879 fix: stop emitting an empty contentBlockStart union for text in ConverseStream (#844)
Real Bedrock emits contentBlockStart only for toolUse blocks; text flows
messageStart -> contentBlockDelta -> contentBlockStop -> messageStop. An
empty start:{} union violates Smithy's exactly-one-member rule and breaks
strict SDK stream consumers (boto3 errors, the AWS Go SDK stalls).
2026-08-20 15:50:38 +10:00
Julian Sommer d4d8fefae6 fix: join the drift loop goroutine before restoring the test timing vars
The drift loop tests launched DriftLoop with a bare go statement and never
joined it, so shrinkDriftTiming's cleanup wrote DriftInterval and
driftBackoffBase while the loop was still reading them in nextDriftBackoff.
Cleanups run LIFO, so registering the join after shrinkDriftTiming makes it
run first and closes the window.
2026-08-20 14:36:49 +10:00
Julian Sommer f765119515 fix: make instance-profile role and tag writes atomic (#842) 2026-08-20 14:32:38 +10:00
Julian Sommer 46f953698f fix: requeue an incomplete reconcile pass instead of waiting a drift interval (#840) 2026-08-20 13:34:39 +10:00
Julian Sommer 7b1890eb8a refactor: use the stdlib uuid package instead of github.com/google/uuid (#841) 2026-08-20 11:57:23 +10:00