# Service interface manifest. # # Source of truth for "which services produce/consume which NATS subjects" # and "which e2e suites cover which services". Consumed by # tests/e2e/select-suites (Bead 4) to derive targeted suite selection from # a PR diff. # # See docs/development/improvements/e2e-targeted-suite-selection.md. # # Editing rules # ------------- # - subscribes / publishes: subjects this service receives / sends. Both # lists use the literal subject string, or a ".*" wildcard for # pattern subscriptions (e.g. "ebs.snapshot.*"). # - depends_on: subjects this service requires from peers. Used by the # selector to compute "if X changes, which suites might break". # - Subjects must match what is actually referenced in source. The lint # in Bead 5 greps `"(ec2|vpc|ebs|s3|iam|elbv2|spinifex)\.[a-zA-Z._-]+"` # and fails if any subject is absent from this manifest. version: 1 services: awsgw: path: spinifex/runtime/roles/awsgw # TLS frontend, SigV4 auth. Pure router: HTTP → NATS request/response. # No subscribes; only publishes (via NATSRequest). subscribes: [] publishes: - "ec2.*" - "elbv2.*" - "acm.*" - "iam.*" - "rds.*" - "ecs.*" - "ochre.vector.*" depends_on: - "ec2.*" - "elbv2.*" - "acm.*" - "iam.*" - "rds.*" - "ecs.*" - "ochre.vector.*" spinifex_daemon: path: spinifex/runtime/roles/spinifex # Orchestrator. Owns most ec2.* + all elbv2.* + iam event publication. # Translates VPC / EBS / S3 operations into vpc.* / ebs.* / s3.* and # forwards to vpcd / viperblockd / predastore. # # Source layout: spinifex/runtime/roles/spinifex (entry), spinifex/daemon # (subscribe registration), spinifex/domains/ and the legacy # spinifex/handlers/{ecs,eks,elbv2,iam,rds} packages (per-subject handler # bodies). Selector treats these paths as belonging to this service via # the `additional_paths` field. additional_paths: - spinifex/daemon - spinifex/domains/ec2/account - spinifex/domains/ec2/eigw - spinifex/domains/ec2/eip - spinifex/domains/ec2/igw - spinifex/domains/ec2/image - spinifex/domains/ec2/instance - spinifex/domains/ec2/key - spinifex/domains/ec2/launchtemplate - spinifex/domains/ec2/natgw - spinifex/domains/ec2/placementgroup - spinifex/domains/ec2/routetable - spinifex/domains/ec2/snapshot - spinifex/domains/ec2/spotinstance - spinifex/domains/ec2/tags - spinifex/domains/ec2/volume - spinifex/domains/ec2/volumestate - spinifex/domains/ec2/vpc - spinifex/handlers/elbv2 - spinifex/domains/acm - spinifex/handlers/iam - spinifex/handlers/ecs - spinifex/handlers/rds - spinifex/domains/rds/subnetgroup - spinifex/domains/rds/parametergroup - spinifex/domains/rds/engine - spinifex/domains/ecs/taskdefinition - spinifex/handlers/eks - spinifex/domains/eks/access - spinifex/domains/eks/addon # Guest add-on wire contract (subject, report, staged-manifest body). - contracts/eks/v1 # ECS agent/scheduler Layer-2 bus wire contract (subjects, message types). - contracts/ecs/v1 - spinifex/domains/ochre/vector subscribes: # EC2 instance lifecycle. - "ec2.RunInstances" - "ec2.DescribeInstances" - "ec2.DescribeStoppedInstances" - "ec2.DescribeTerminatedInstances" - "ec2.DescribeInstanceAttribute" - "ec2.DescribeInstanceStatus" - "ec2.DescribeInstanceTypes" - "ec2.ModifyInstanceAttribute" - "ec2.ModifyVolume" - "ec2.start" - "ec2.terminate" - "ec2.launch" # contracts/ec2/v1 owns the command payload and this subject vocabulary. # The wildcard is intentionally one final instance-ID token, not `>`. - "ec2.cmd.*" # EC2 VPC / network ops handled by daemon before delegation to vpcd. - "ec2.CreateVpc" - "ec2.DescribeVpcs" - "ec2.CreateSubnet" - "ec2.CreateInternetGateway" - "ec2.CreateKeyPair" - "ec2.CreateImage" - "ec2.DescribePlacementGroups" # Cluster coordination subjects. - "ec2.ReserveClusterNode" - "ec2.FinalizeClusterInstances" # An EIP association reaches whichever node holds the instance, which is # rarely the node that served the request (fan-out, no queue group). - "ec2.eni.public-ip-changed" # contracts/cluster/v1 owns the node status/capacity payload and subject. - "spinifex.node.status" # contracts/cluster/v1 owns the VM inventory payload and subject. - "spinifex.node.vms" - "spinifex.nodes.discover" - "spinifex.cluster.shutdown.progress" # Coordinated cluster-shutdown phases (fan-out, no queue group). - "spinifex.cluster.shutdown.gate" - "spinifex.cluster.shutdown.drain" - "spinifex.cluster.shutdown.storage" - "spinifex.cluster.shutdown.persist" - "spinifex.cluster.shutdown.infra" # Operator-plane Predastore topology contract (contracts/operator/v1). - "spinifex.storage.config" # ELBv2 control plane (handlers/elbv2/service_nats.go topic map). - "elbv2.CreateLoadBalancer" - "elbv2.DeleteLoadBalancer" - "elbv2.DescribeLoadBalancers" - "elbv2.CreateTargetGroup" - "elbv2.DeleteTargetGroup" - "elbv2.DescribeTargetGroups" - "elbv2.RegisterTargets" - "elbv2.DeregisterTargets" - "elbv2.DescribeTargetHealth" - "elbv2.CreateListener" - "elbv2.DeleteListener" - "elbv2.DescribeListeners" # ACM-alike certificate store (domains/acm; subscribed in daemon.go). - "acm.ImportCertificate" - "acm.DescribeCertificate" - "acm.ListCertificates" - "acm.DeleteCertificate" - "elbv2.AddListenerCertificates" - "elbv2.RemoveListenerCertificates" - "elbv2.DescribeListenerCertificates" - "elbv2.DescribeSSLPolicies" # RDS control plane (handlers/rds/subjects.go). One wildcard rather than # 30 literals: every action subject is registered from a constant, so a # per-subject list would be a hand-mirror of that file with nothing # checking it. - "rds.*" # ECS control plane (handlers/ecs/service_nats.go, registered in # daemon.go). One wildcard, as for RDS: every action subject is a literal # in those two files, so a per-subject list would only mirror them. - "ecs.*" # Layer-2 agent bus: register and health are wildcard subscriptions over # {account}.{db-instance}, so the subject is built at runtime and only the # prefix is declarable. - "rds.bus.*" # ECS Layer-2 scheduler/agent bus (contracts/ecs/v1): account, cluster and # instance/task IDs are runtime-built, so only the prefix is declarable. # Only the leader holds these subscriptions, so there is no queue group. - "ecs.bus.*" # Ochre vector store tenant surface (domains/ochre/vector/api.go), # gated behind config.OchreVectorConfig.Enabled (default off). - "ochre.vector.createIndex" - "ochre.vector.deleteIndex" - "ochre.vector.listIndexes" - "ochre.vector.ingest" - "ochre.vector.describeJob" - "ochre.vector.query" - "ochre.vector.listJobs" - "ochre.vector.stopJob" # Fence route owned by contracts/viperblockd/legacy/v1: viperblockd # announces a torn-down export so this node stops the guest that was # using it. "ebs.fenced" is the single-node form; a named node is # addressed as "ebs..fenced". - "ebs.fenced" publishes: # → vpcd - "vpc.create" - "vpc.delete" - "vpc.create-subnet" - "vpc.delete-subnet" - "vpc.create-sg" - "vpc.delete-sg" - "vpc.update-sg" - "vpc.create-port" - "vpc.delete-port" - "vpc.update-port-sgs" # EC2-to-network projection owned by contracts/ec2/v1. The network # subscriber realizes it in OVN without reading EC2 private state. - "vpc.igw-attach" - "vpc.igw-detach" - "vpc.add-nat-gateway" - "vpc.delete-nat-gateway" - "vpc.add-nat" - "vpc.delete-nat" - "vpc.dhcp.acquire" - "vpc.dhcp.release" # Per-subnet route-table projections owned by domains/ec2/routetable: # IGW default-route reachability and the egress gate/ungate decision it # implies, published fire-and-forget on every route-table mutation. - "vpc.add-igw-route" - "vpc.delete-igw-route" - "vpc.gate-subnet-egress" - "vpc.ungate-subnet-egress" # Egress-only reroute removal for a single system instance # (handlers/eks). Fire-and-forget. vpc.add-system-egress has no # current producer. - "vpc.delete-system-egress" # → viperblockd # The migrated legacy ebs.config and ebs.sync payloads and routes are # owned by contracts/viperblockd/legacy/v1. They are transitional and # distinct from the generic providers/ebs protocol; other legacy ebs.* # routes remain to be migrated separately. - "ebs.mount" - "ebs.unmount" - "ebs.delete" - "ebs.sync" - "ebs.snapshot.*" - "ebs.config" - "ebs.config.*" # Abandon route owned by contracts/viperblockd/legacy/v1: the daemon # asks viperblockd to tear an export down without sealing. "ebs.abandon" # is the single-node form; a named node addresses "ebs..abandon". - "ebs.abandon" # → predastore (S3 / AMI / keypair store) - "s3.*" # → every daemon, so the one holding the instance reports the new address - "ec2.eni.public-ip-changed" # IAM lifecycle event - "iam.account.created" # → the in-guest rds-agent: set-password and the like, published on the # per-instance command subject and answered on its reply subject. - "rds.bus.*" # ECS service-reconcile self-wake (contracts/ecs/v1): the worker that # recorded a task-state change is rarely the leader, so this fans the # wake out and the leader's own "ecs.bus.*" subscription picks it up. - "ecs.bus.*" depends_on: - "vpc.*" - "ebs.*" - "s3.*" vpcd: path: spinifex/runtime/roles/vpcd # OVN-backed VPC / subnet / SG / NAT / IGW data plane. subscribes: - "vpc.create" - "vpc.delete" - "vpc.create-subnet" - "vpc.delete-subnet" - "vpc.create-sg" - "vpc.delete-sg" - "vpc.update-sg" - "vpc.create-port" - "vpc.delete-port" - "vpc.update-port-sgs" - "vpc.igw-attach" - "vpc.igw-detach" - "vpc.add-nat-gateway" - "vpc.delete-nat-gateway" - "vpc.add-nat" - "vpc.delete-nat" - "vpc.dhcp.acquire" - "vpc.dhcp.release" - "vpc.add-igw-route" - "vpc.delete-igw-route" - "vpc.gate-subnet-egress" - "vpc.ungate-subnet-egress" - "vpc.add-system-egress" - "vpc.delete-system-egress" publishes: - "vpc.port-status" - "vpc.dhcp.lease-changed" - "vpc.dhcp.owner-check" depends_on: [] viperblockd: path: spinifex/runtime/roles/viperblockd # EBS data plane. Backed by the viperblock storage engine in the # mulga/viperblock submodule. Submodule pointer bump → selector forces # "run all" (contract changes can be invisible at the spinifex source # level). # The migrated legacy ebs.config and ebs.sync routes are versioned by # contracts/viperblockd/legacy/v1, not by providers/ebs. Other legacy # ebs.* routes remain to be migrated separately. # The dirty-marker JetStream KV bucket VIPERBLOCK_VOLUME_DIRTY is owned # by the same contract; not a NATS subject, so not listed below. # # Source layout: spinifex/runtime/roles/viperblockd (role: process lifecycle, # NATS subscription wiring) and spinifex/providers/ebs/viperblock (the # Viperblock EBS provider adapter the role wires up). Selector treats the # adapter path as belonging to this service via `additional_paths`. additional_paths: - spinifex/providers/ebs/viperblock subscribes: - "ebs.mount" - "ebs.unmount" - "ebs.delete" - "ebs.sync" - "ebs.snapshot.*" - "ebs.config" - "ebs.config.*" # Abandon route (contracts/viperblockd/legacy/v1): request/reply from # the daemon, answered by the Viperblock adapter on the named node. - "ebs.abandon" publishes: - "ebs.mount.response" # Fence route (contracts/viperblockd/legacy/v1): announced after the # Viperblock adapter tears an export down because its lease moved. - "ebs.fenced" - "ebs.unmount.response" depends_on: [] predastore: path: spinifex/runtime/roles/predastore # S3 / AMI / keypair storage. External submodule under mulga/predastore; # this dir contains only the spinifex-side adapter. Submodule pointer # bump → selector forces "run all". subscribes: - "s3.*" publishes: [] depends_on: [] nats: path: spinifex/runtime/roles/nats # NATS broker bootstrap / cluster config. Not a NATS pub/sub # participant. Any change here is infra-path; selector forces # "run all". subscribes: [] publishes: [] depends_on: [] spinifexui: path: spinifex/runtime/roles/spinifexui # Web UI. Talks to awsgw over HTTP, never NATS. Diffs limited to this # dir do not need any e2e suite to run. subscribes: [] publishes: [] depends_on: [] covers_no_suite: true # Fixture → service mapping. Each Ensure* helper in # tests/e2e/harness/fixtures.go exercises this set of services; the # Bead 4 selector AST-scans test files for harness.Ensure* call sites # and resolves coverage via this block. fixtures: EnsureKeyPair: services: [spinifex_daemon, predastore] EnsureAMI: services: [spinifex_daemon, predastore, viperblockd] EnsureDefaultVPC: services: [vpcd, spinifex_daemon] EnsureSubnet: services: [vpcd, spinifex_daemon] EnsureSG: services: [vpcd, spinifex_daemon] EnsureInstance: services: [spinifex_daemon, viperblockd, vpcd] EnsureVolume: services: [spinifex_daemon, viperblockd] EnsureSnapshot: services: [spinifex_daemon, viperblockd, predastore] EnsureNATGateway: services: [vpcd, spinifex_daemon] EnsureLoadBalancer: services: [spinifex_daemon, awsgw] suites: e2e-cert: path: tests/e2e/cert covers: [awsgw, spinifex_daemon, vpcd, predastore] e2e-lb: path: tests/e2e/lb covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # Exercises full ELBv2 control plane (handlers/elbv2) + data plane # (LB agent on the system VM). ELBv2 is part of spinifex_daemon in # this manifest; revisit if it splits into a standalone service. e2e-single: path: tests/e2e/single covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # Full single-node lifecycle: VPC, AMI, keypair, instance, volume, # snapshot, IAM. Currently runs all phases sequentially under one # `TestSingleNode`. Bead 3 (single-fixture-migration) splits this # into per-test fixture-driven coverage so the selector can pick # subtests, not just whole suites. e2e-iam: path: tests/e2e/iam covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # "Everything IAM": the IAM/STS control plane plus the instance-identity # tests (instance-profile association + IMDS), which boot guest VMs — OVN # via vpcd, AMI via predastore, root volume via viperblockd, lifecycle via # spinifex_daemon. Single-mode, VM-booting leg. e2e-ecr: path: tests/e2e/ecr covers: [awsgw, spinifex_daemon, predastore] # ECR control plane (domains/ecr, gateway/ecr*) + OCI data plane: blobs and # manifests in predastore, control + auth bridge in awsgw, KV via # spinifex_daemon. docker/crane/skopeo data-plane subtests skip when the # client binary or registry-host resolution is absent. e2e-bedrock: path: tests/e2e/bedrock covers: [awsgw] # Ochre (AWS-Bedrock) control plane: static catalog list/get served # straight from awsgw (gateway/bedrock.go, gateway/bedrock/) — no NATS # hop to spinifex_daemon. Converse (Anthropic-direct / self-host vLLM) # subtests are gated on backend availability and skip by default. e2e-ecs: path: tests/e2e/ecs covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # ECS data plane end-to-end: cluster + a real container instance launched # from the spinifex-ecs-node AMI (boots, registers over the gateway, runs # tasks through containerd), standalone tasks in awsvpc/bridge/host modes, # and an awsvpc service fronted by an ELBv2 target group. Boots a guest VM, # so vpcd (OVN + awsvpc ENI), viperblockd (root volume), predastore (AMI) # are all in the path. e2e-rds: path: tests/e2e/rds covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # RDS create/describe/connect: CreateDBInstance boots a dual-NIC DB VM from # the engine AMI (predastore) with a Viperblock data volume (viperblockd) and # a customer ENI (vpcd), and the reconciler flips it to available on the # in-guest agent's first healthy heartbeat over awsgw. Every test deletes the # instances it creates, so the suite needs no gate beyond SPINIFEX_E2E. e2e-ddil: path: tests/e2e/ddil covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # DDIL = degraded / disconnected / intermittent / low-bandwidth. # Exercises the full daemon stack under network failure injection; # any service touching the wire path is in scope. # Suites below are bash-only today. Entries activate as the Go ports # land (Bead 2 fixtures unblock new-shape ports for multinode etc.). # # e2e-multinode: # path: spinifex/tests/e2e/multinode # covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # e2e-reboot: # path: spinifex/tests/e2e/reboot # covers: [spinifex_daemon, viperblockd] # e2e-tofu: # path: spinifex/tests/e2e/tofu # covers: [awsgw, spinifex_daemon, vpcd] # e2e-bm: # path: spinifex/tests/e2e/baremetal # covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # # e2e-natuplink activates once CI can provision an external_mode=nat # node (setup-ovn.sh --nat-uplink + spx admin init --external-mode=nat). # e2e-natuplink: # path: spinifex/tests/e2e/natuplink # covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore] # Special-case rules consumed by the selector (Bead 4). Hard-coded in # the tool, listed here for visibility. # # infra_paths_force_all: # - spinifex/runtime/roles/nats/** # - spinifex/tests/e2e/harness/** # - spinifex/tests/e2e/lib/** # - spinifex/.github/workflows/e2e-go.yml # - spinifex/.github/scripts/** # - spinifex/Makefile # - mulga/scripts/tofu-cluster/** # # submodule_force_all: # - mulga/predastore (submodule pointer) # - mulga/viperblock (submodule pointer) # # many_services_force_all_threshold: 3 # ≥ N services touched → run all