Commit Graph

1741 Commits

Author SHA1 Message Date
Julian Sommer 2a79bc8473 fix: record RDS instance creation events (#734) 2026-08-05 15:17:06 +10:00
Julian Sommer f7d5d97e3f fix: propagate RDS parameter group edits (#733) 2026-08-05 15:08:14 +10:00
Julian Sommer df34f27cdf fix: restart RDS after failed storage grow (#732) 2026-08-05 14:48:16 +10:00
Julian Sommer 68671cdf19 fix: prevent parameter groups bricking RDS instances (#731) 2026-08-05 14:16:31 +10:00
Julian Sommer f240c33778 fix: wait for postgres to be up before running tofu workbook e2e 2026-08-05 11:48:58 +10:00
Julian Sommer e5f528f877 chore: add rds image to publish 2026-08-05 11:03:35 +10:00
Julian Sommer 7f349a8e3d fix: report RDS client errors accurately (#730) 2026-08-05 11:00:15 +10:00
Julian Sommer 710169e5a6 fix: eliminate RDS Terraform drift (#729) 2026-08-05 10:34:51 +10:00
Julian Sommer d2d0b357a9 fix: tofu e2e 2026-08-05 10:05:45 +10:00
Julian Sommer 47e6b824ad fix: guard RDS create rollback revisions (#728) 2026-08-05 09:48:02 +10:00
joshsiv-mulga 54434dc22e Update README.md 2026-08-05 09:42:46 +10:00
joshsiv-mulga 58b1731bfd Update README.md 2026-08-05 09:35:13 +10:00
joshsiv-mulga fcb211cfd9 Update README.md 2026-08-05 09:30:32 +10:00
joshsiv-mulga b37708be09 Update README.md 2026-08-05 09:26:14 +10:00
joshsiv-mulga 3193933ce8 Update README.md 2026-08-05 09:24:45 +10:00
Julian Sommer 4b9039c76f fix: retain automated backups after RDS restore (#727) 2026-08-05 09:19:03 +10:00
joshsiv-mulga b977e752e0 Update README.md 2026-08-05 09:12:19 +10:00
joshsiv-mulga f49caedd31 Update README.md 2026-08-05 09:10:49 +10:00
Julian Sommer 8765077f4f fix: cancel RDS modifies when lease is lost (#694) 2026-08-04 16:44:56 +10:00
Julian Sommer d8432e99a2 Reapply "fix(migrate): keep predastore's data dir owned by its service user"
This reverts commit 317f714355.
2026-08-04 16:13:28 +10:00
Julian Sommer 871513dc6c Reapply "feat: match new predastore config surface area (#591)"
This reverts commit b4701c78de.
2026-08-04 16:13:28 +10:00
Julian Sommer ef8bdce9e9 release: update go.mod for v1.15.0 v1.15.0 2026-08-04 15:26:46 +10:00
Julian Sommer b4701c78de Revert "feat: match new predastore config surface area (#591)"
This reverts commit c2b80a9b93.
2026-08-04 15:02:39 +10:00
Julian Sommer 317f714355 Revert "fix(migrate): keep predastore's data dir owned by its service user"
This reverts commit 576742dfeb.
2026-08-04 14:58:06 +10:00
Julian Sommer d9907fbf13 fix: scope CreateDBSnapshot and RestoreDBInstanceFromDBSnapshot for authorization
Both actions carried no scope, so their policy check evaluated against "*".
Matching is pattern-to-value, so a statement whose Resource is a specific ARN
never matches "*" — a Deny fencing an instance blocked the scoped
ModifyDBInstance and DeleteDBInstance but not a snapshot of the same instance.
The principal could snapshot the fenced instance, restore it under a name the
Deny does not cover, set a master password of their choosing and read the data.

Each is scoped to its source: the instance for a create, the snapshot for a
restore, which breaks the chain at its first step. Both identifiers are required
on their action and ResourceARN already falls back to "*" when absent, so this
narrows where the identifier is present and is a no-op where it is not.

Evaluating both ARNs per action, for AWS parity, is deliberately left out: it
turns the scope into a slice across all 13 scoped entries and touches the shared
dispatcher call site.
2026-08-04 14:47:18 +10:00
Julian Sommer 615578a2e7 refactor: simplify RDS internals (#692) 2026-08-04 14:42:45 +10:00
Julian Sommer 2e09461080 chore: cleanup code comments 2026-08-04 14:20:19 +10:00
Josh Siviour 43975b01e6 feat: add spx admin ochre endpoint ensure/describe/list/delete (#691)
* feat: add spx admin ochre endpoint ensure/describe/list/delete

The daemon subscribes all four bedrock.endpoint.* subjects, but nothing in
production calls their NATS client, so the serving-endpoint lifecycle could
not be exercised at all in a running system. This gives an operator a direct
way to drive it.

ensure --wait polls describe until READY and reports the elapsed time, since
the daemon replies STARTING as soon as it claims the model and the launch
outlives the request. That is the measured cold start.

Also corrects the weights stage tokenizer rule in COMMANDS.md, which still
described both tokenizer files as required rather than either.

* test: cover the ochre endpoint Run wrappers' connect and exit paths

* fix: treat an empty endpoint bucket as no endpoints, not an error

ListEndpoints was the only kvutil.Keys caller not handling ErrNoKeysFound,
so 'ochre endpoint list' failed on a cluster that had never launched one.
2026-08-04 14:20:02 +10:00
Josh Siviour 49c5a04bed fix: register ochre weights snapshot with the EC2 control plane (#690)
weights stage created the snapshot at the viperblock layer only, writing
the block checkpoint and config.json but not the metadata.json that
CreateVolume resolves a SnapshotId through. The endpoint launcher clones
the weights volume via ec2.CreateVolume, so every launch failed with
InvalidSnapshot.NotFound against a snapshot that was present on disk.
2026-08-04 14:09:40 +10:00
Julian Sommer 050dbdb060 fix: disallow reserved master names during rds password apply 2026-08-04 13:49:40 +10:00
Julian Sommer 970bf18ab5 fix: guard a half bootstapped datadir against crashes 2026-08-04 13:42:16 +10:00
Josh Siviour 12a1e4aedc fix: correct the GPU model catalog and stop unknown VRAM reading as zero (#688)
The GPU model table was only ever used to report VRAM, so wrong or missing
entries were cosmetic. Bedrock's capacity check is the first code that rejects
on VRAM, which turns every latent data error into an admission decision.

Correct seven entries verified against pci.ids — most seriously 1002:7448,
labelled Instinct MI300X at 192GB when it is a Radeon Pro W7900 at 48GB, a
4x over-report that admits a model which then OOMs at weight load. Record the
smallest capacity where one PCI ID spans several cards, since under-reporting
refuses an endpoint an operator can see and override while over-reporting
fails later and looks like a vLLM bug.

Widen the table so common datacenter, workstation and consumer cards resolve
without an operator-supplied override, and apply GPUModelOverrides to the pool
devices as well as the advertised model, so the override reaches the code that
actually gates. An override with no memory_mib is left alone rather than
wiping discovered VRAM, since the config cannot distinguish it from a
deliberate zero.

MemoryMiB 0 stays the "undiscoverable" sentinel, but checkCapacity no longer
counts it as a device with no VRAM: an otherwise-free device now reports which
card is unknown and how to fix it.
2026-08-04 13:36:54 +10:00
Julian Sommer 5a3f5e60b2 fix: cleanup COMMANDS.md 2026-08-04 12:00:57 +10:00
Julian Sommer 14e24243d8 fix: rds e2e assertion 2026-08-04 11:29:20 +10:00
Julian Sommer 8cf44f3310 fix: harden RDS beta provisioning and replies (#689) 2026-08-04 11:27:54 +10:00
Julian Sommer faa0519cf1 fix: the RDS master user is no longer a PostgreSQL superuser (#687) 2026-08-04 10:59:45 +10:00
Josh Siviour 175b8b93d9 fix: make the predastore readiness gate a real readiness probe (#685)
* fix: make the predastore readiness gate a real readiness probe

checkPredastoreReady only did a TCP dial + close, so a listening socket
satisfied it well before predastore could serve TLS. In CI this passed
in 170 microseconds, 0.72s after predastore opened its listener, and
recovery started against a backend that wasn't serving yet — predastore
logged the probe itself as a TLS handshake EOF.

Replace the dial with an HTTPS GET against the configured predastore
endpoint on a package-level client (hoisted out of the 2s poll loop),
with a per-request 3s context timeout. Any HTTP response counts as
ready, including 403 from this deliberately unsigned request, which is
exactly what an unauthenticated S3 GET returns. Only a transport or TLS
failure means not-ready.

This narrows the race window and removes the spurious TLS-handshake-EOF
log lines predastore emits on every daemon start, but it does not close
the window on its own: predastore can serve TLS before its storage
backend answers reads correctly. The load-bearing fix is the viperblock
VBState read retry landing separately.

* style: trim the predastore readiness comments to three lines
2026-08-04 10:34:06 +10:00
Julian Sommer 9dfef26e48 fix: rds-init 2026-08-04 10:24:48 +10:00
Ben Duncan b76aab8b50 feat(installer): three-plane networking, ZFS root, distributed NAT and multi-node install (#663) 2026-08-04 10:21:12 +10:00
Julian Sommer f3586ed32c docs: add the RDS quickstart Terraform workbook (#686) 2026-08-04 09:54:41 +10:00
Josh Siviour 6147ca9541 fix(ec2): tell the caller when a launch key pair cannot decrypt a password (#684)
GetPasswordData returned an empty string both when the guest had not emitted a
password yet and when it never could, because the launch key pair was ED25519
and cannot perform PKCS#1 v1.5 encryption. The two are indistinguishable, so a
caller in the second case polls forever with no signal.

An empty result on a Windows instance whose launch key is not RSA now returns
InvalidKeyPair.Type naming the cause and the fix. Every uncertainty keeps the
old empty result: a populated blob always wins, Linux is never judged, and an
unresolvable key is not treated as evidence of the wrong type.
2026-08-03 22:57:47 +10:00
Josh Siviour 94ca4dd72c fix(ec2): return RSA key pairs in PKCS#1 PEM and add a UI key-type selector (#683)
get-password-data --priv-launch-key requires PEM, but ssh-keygen writes the
OpenSSH container by default, so even an explicitly-RSA key pair failed with
"Unable to decrypt password data using provided private key file" — which
reads like a padding problem rather than a format one. AWS returns PKCS#1 PEM
for RSA key pairs. OpenSSH reads that format for SSH either way.

The create-key-pair UI gains a key-type selector, defaulting to the rsa the
page already sent, with a hint that RSA is required for Windows password
retrieval.

The API default stays ed25519. Changing it is a separate decision.
2026-08-03 22:30:42 +10:00
Josh Siviour 46472082f9 fix(bedrock): give ResourceNotFoundException Bedrock wording and keep the embedding logging flag (#682)
ResourceNotFoundException is a shared wire code, so Bedrock aliases the EKS
error constant and inherited its message: a caller asking for a model was
told to list its EKS clusters. Register Bedrock overrides in the existing
errorLookupByService table, under both service keys since the gateway splits
bedrock from bedrock-runtime on the /model/ path prefix.

PutModelInvocationLoggingConfiguration also accepted embeddingDataDeliveryEnabled
and dropped it: the internal LoggingConfig had no such field, so the flag never
survived to Get. Carry it through both translation directions and fold it into
the bucket-required check, since embedding delivery with no bucket is as
undeliverable as text or image.
2026-08-03 22:09:38 +10:00
Josh Siviour b67d8878c1 fix(ec2): default Windows AMIs to HttpTokens=optional at launch (#681)
cloudbase-init has no IMDSv2 token support in any release, so a Windows
instance launched without an explicit MetadataOptions block 401s on every
metadata GET and never reaches its main plugin stage. Derive the launch
default from the resolved image's platform: Windows gets optional, every
other platform keeps required, and an explicit request value always wins.
2026-08-03 21:29:38 +10:00
Josh Siviour f3dd3c251e feat: add spx admin ochre weights stage/list/remove (#680)
* feat: add spx admin ochre weights stage/list/remove

Give operators a supported path to stage a self-hosted model's
weights: point at a predastore S3 prefix holding ordinary Hugging
Face files, and stage materialises them into a viperblock volume,
snapshots it, and records the source URI and snapshot ID in the
bedrock-weights KV bucket.

stage refuses before downloading anything if the model is not a
self-host catalog entry, or if the prefix is missing any required
file. Re-staging the same source is a no-op; a different source
replaces the entry and reports the previous snapshot for separate
reclamation.

WeightsStore gains GetWeights and a source-URI field on its record
to support this. LookupServingSpec (catalog.go) is reused unchanged
to validate --model-id.

* fix: unify ServingSpec after rebase onto dev's daemon-launcher spec

Rebasing onto dev pulled in mulga-vmfng.7.5's daemon-side ServingSpec
and LookupServingSpec, textually duplicating the ochre weights stage
CLI's own versions in catalog.go. Collapse both into one type carrying
ModelID and VLLMArgs, and one three-value LookupServingSpec so callers
distinguish an unknown model (found=false) from a provider-tier one
(selfHost=false). Update the daemon launcher's call site accordingly.

* test: cover ochre weights stage/list/remove command behaviour

Splits runOchreWeightsStage/List/Remove into testable decision functions
(runStageWeights, listWeightsOutput, removeWeights) separate from cobra and
NATS wiring, and makes mkfs.ext4 execution and the connect/exit steps
swappable so the full command flow can be driven against an embedded
JetStream server instead of live infrastructure. Also relaxes the tokenizer
file requirement to accept either tokenizer.json or tokenizer.model, since
canonical Llama 3.x repos ship only the former at the prefix root.

* fix: resolve mkfs.ext4 from sbin when it is absent from PATH

e2fsprogs installs mkfs.ext4 into sbin, which a non-login shell omits, so
weights staging failed on an unprivileged operator's otherwise fine host.

* fix: size the weights volume to a whole GiB so the tail block fits

viperblock rejects a write whose block overruns VolumeSize, so importing an
image of unaligned length failed on its last block. Rounding up also makes
the volume's byte size and its manifest SizeGiB describe the same volume.
Name the volume in the import error so a part-written import is traceable.
2026-08-03 21:14:47 +10:00
Josh Siviour 8be25c93f9 fix: surface faults on the AMI-read and service-ownership paths (#679)
DescribeImages skipped any ami-* prefix whose config.json could not be
fetched, read, or parsed, logging each at DEBUG. At the daemon's default
INFO level the result was an empty Images list and a completion line
reading count=0, indistinguishable from a healthy empty registry.

Promote the three fault branches plus the body-close failure to WARN,
leave NoSuchKey at DEBUG since a prefix with no config is a legitimate
non-AMI directory, and report prefixes examined alongside images
returned so a mismatch is visible without raising the log level.

SetServiceOwnership chowned per-service directories to service users
without propagating a missing user, so spx admin init reported success
while leaving /etc/spinifex/northstar root-owned 0600. ChownRecursive
and SetServiceOwnership now return errors, failures are aggregated
across every service rather than stopping at the first, and
finalizeNodeSetup prints the unapplied paths instead of a success mark.
2026-08-03 21:07:11 +10:00
Josh Siviour da5e38cf18 fix(imds): answer ARP for the captured link-local addresses (#678)
The per-tap datapath only matched IP, on the assumption that a guest reaches
169.254.169.254 through its default gateway. A guest that treats 169.254.0.0/16
as on-link resolves the address itself instead, and nothing on br-imds, br-int
or in OVN answered that ARP, so it could not reach IMDS at all.

Windows does exactly this, which left cloudbase-init timing out on every
metadata request and skipping the rest of its plugin stage.

Install a per-tap ARP responder for each captured address under the existing
cookie, replying with the gateway MAC the egress flow already presents.
2026-08-03 17:26:11 +10:00
Josh Siviour b5fa0ffb7a fix: stop viperblockd discarding the configured S3 region (#677)
The viperblock start command applies --s3-host, --s3-bucket and --s3-region over
spinifex.toml whenever viper reports a non-empty value. s3-bucket and s3-region
carried non-empty defaults, so the override fired on every start even though the
service unit passes neither flag nor environment variable, and the region read
from the config was replaced with ap-southeast-2.

Any cluster deployed to another region could not launch an instance: viperblockd
signed its S3 requests with ap-southeast-2, predastore verified them against its
own region and rejected them as AuthorizationHeaderMalformed, the volume mount
failed and RunInstances ended in launch_failed. The bare-metal e2e cell, which
installs with --region bare-metal, failed every instance-backed test this way.

Default all three to empty, matching s3-host, so an unset flag leaves the
configured value alone while an explicit flag or SPINIFEX_VIPERBLOCK_* still
wins. The predastore.toml template also pinned the main bucket to a literal
ap-southeast-2 while the top-level key and the northstar bucket both rendered
the configured region; it now renders it too.
2026-08-03 15:50:48 +10:00
Julian Sommer 6605d049bc chore: fix COMMANDS.md 2026-08-03 15:47:43 +10:00
Julian Sommer 53960e0bc4 fix: update README.md to mark RDS as Available 2026-08-03 15:46:23 +10:00