From c2dfba8a60047e2b059de78dd852f3af00345118 Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Fri, 9 Oct 2026 14:51:38 +1000 Subject: [PATCH] docs: record operator/admin split in package-boundary migration --- architecture/layering/allowlist_test.go | 2 +- docs/PACKAGE_BOUNDARY_MIGRATION.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/architecture/layering/allowlist_test.go b/architecture/layering/allowlist_test.go index 768edf60e..b6bf2dbb1 100644 --- a/architecture/layering/allowlist_test.go +++ b/architecture/layering/allowlist_test.go @@ -154,7 +154,7 @@ var allowlist = []allowance{ {From: "handlers/elbv2", To: "lbagent", Debt: "handlers/elbv2/{agent_types,haproxy,health_checker,nginx}.go import lbagent", Reason: "health-report and cert/PID-path vocabulary; needs a guest/controller contract"}, {From: "handlers/elbv2", To: "utils", Debt: "utils", Reason: "consumes a recorded utils residual file awaiting its owner"}, - {From: "handlers/iam", To: "admin", Debt: "handlers/iam/service_impl.go:30 imports admin", Reason: "credential and account helpers; blocked with admin -> operator/admin"}, + {From: "handlers/iam", To: "admin", Debt: "handlers/iam/service_impl.go:30 imports admin", Reason: "credential and account helpers; awaits an IAM-credentials owner"}, {From: "handlers/rds", To: "domains/dns", Debt: "close-out: handlers/rds (ADR-0004 blocked-dependency list; inventory-rds boundary imports)", Reason: "legacy RDS calls another domain's or runtime implementation directly"}, {From: "handlers/rds", To: "domains/ec2/instance", Debt: "close-out: handlers/rds (ADR-0004 blocked-dependency list; inventory-rds boundary imports)", Reason: "legacy RDS calls another domain's or runtime implementation directly"}, diff --git a/docs/PACKAGE_BOUNDARY_MIGRATION.md b/docs/PACKAGE_BOUNDARY_MIGRATION.md index b62dc208f..8f270b516 100644 --- a/docs/PACKAGE_BOUNDARY_MIGRATION.md +++ b/docs/PACKAGE_BOUNDARY_MIGRATION.md @@ -316,6 +316,7 @@ while the branch is in flight; they do not describe the umbrella repository's | `919d5bfa6` | `spinifex/migrate` | `spinifex/operator/upgrade` | Owner decision: the operator-driven config-file and object-store upgrade workflow behind `spx admin upgrade` belongs to `operator/upgrade`, distinct from `foundation/state/migrate`. Package renamed to `upgrade`; no registry types, migration names, versions, file formats or command names changed. It imports only `foundation/state/{kvutil,migrate}`, `providers/objectstore` and jetstream, so it carries no layering debt. `scripts/diff-coverage.sh` and `VERSIONS.md` point at the new path. Full preflight passed apart from the recorded Go 1.27.0 advisory exception. | | `5292bd22c` | — | — | ADR-0001 layering ratchet added, with the nine import edges it found missing from this record recorded below; it lives at `architecture/layering` beside `tests/`, outside the product tree. | | `8bf162fb7` | `spinifex/utils/ec2_subject.go` (`SubjectEnsureDefaultVpc`) | `contracts/ec2/v1` (`EnsureDefaultVpcSubject`) | The default-VPC request/reply subject crosses the gateway–daemon process boundary, so its home is the EC2 contract. Only the subject moved; the request and reply bodies remain anonymous structs on each side, as the contract README states. | +| `6b01f39e7` | `spinifex/admin` (config generation, certificate issuance, host discovery, telemetry, email validation, image import/removal, orphan volumes) | `spinifex/operator/admin` | These are operator use cases consumed only by `operator/cli` and `operator/installer/ui`. Certificate issuance moves as an operator use case; `foundation/pki` keeps generic CA-key loading and the tenant ACM CA stays in `domains/acm`. Legacy `admin` keeps `DefaultAccountID`/`DefaultAccountName`, `GenerateAWSAccessKey`/`GenerateAWSSecretKey` and `images_promote.go`; the private `readAMI` helper and the removal test fixtures are duplicated there rather than shared. No forwarding shim; `operator/cli` imports the legacy package as `legacyadmin`. Full preflight (excluding the recorded Go 1.27.0 advisories) passed. | The EC2 contract row is intentionally different from the directory moves: it creates a compatibility boundary. `ec2.cmd.*` retains its deployed one-token NATS @@ -388,10 +389,10 @@ These residuals were recorded by individual slices and must be resolved once the - Domain → runtime: `domains/ochre/capacity.go:9` imports `runtime/compute/gpu`; `domains/ec2/volume/service_impl.go:30` and `volume_leak_reaper.go:8` import `runtime/compute/vm`; `domains/ec2/instance/status_selection.go:9` also imports `runtime/compute/vm`. Each reads VM or GPU runtime state directly instead of through a domain-owned projection. - `handlers/sts/oidc_jwks.go:14` imports `handlers/eks` for `AccountBucketName` (line 101) and `OIDCJWKSKey` (line 110), the Q-22 STS-to-EKS dependency ADR-0001 prohibits in its present form. Removal condition: an EKS-owned OIDC projection or an identity-owned contract that STS consumes instead of the EKS implementation package. - `handlers/elbv2/{agent_types,haproxy,health_checker,nginx}.go` import `lbagent` (`HealthReport`, `ServerStatus`, `CertDir`, `NginxPIDPath`, `NginxCertDir`), a domain reading an agent's implementation package rather than a declared wire contract. Removal condition: the health-report and cert/PID-path vocabulary moves behind a guest/controller contract the domain and the agent both consume. -- `handlers/iam/service_impl.go:30` imports `admin` for `GenerateAWSSecretKey` (line 641) only, a domain-to-operator edge; the `admin.AccountID`, `admin.AccountName` and `admin.EncryptedSecret` uses are fields of a local `*AdminBootstrapData` parameter, not the package. This is part of the already-recorded `admin` → `operator/admin` blocker (`GenerateAWSAccessKey`/`GenerateAWSSecretKey` need an IAM-credentials owner before `admin` can move). +- `handlers/iam/service_impl.go:30` imports `admin` for `GenerateAWSSecretKey` (line 641) only, a domain-to-operator edge; the `admin.AccountID`, `admin.AccountName` and `admin.EncryptedSecret` uses are fields of a local `*AdminBootstrapData` parameter, not the package. It clears with the remaining `admin` entry, once the key generators have an IAM-credentials owner. - `vpcd/imds_instance_state.go:10` imports `daemon` for `LocalState`, `ReadLocalState`, `LocalStatePath`, `NewJetStreamManager` and `InstanceStateBucket`, a guest-facing daemon component reading the daemon's own implementation rather than a declared local-state contract. Removal condition: a daemon-owned local-state capability or contract that vpcd consumes instead of the `daemon` package directly. - Advisory: 27 production files across `domains/{acm,dns,ec2/*,network/host,network/ovn,ochre}` import `bootstrap/config` directly rather than through typed constructor inputs, which ADR-0001's `config` row expects. `bootstrap/preflight/preflight.go:15` imports `runtime/host/command` and `services.go:13` imports `operator/host/systemd`; both are expected bootstrap-to-runtime/operator edges for startup validation and are not domain-rule violations. -- `admin` → `operator/admin` is blocked until its shared pieces have owners: `DefaultAccountID`/`DefaultAccountName` (IAM/account bootstrap), `GenerateAWSAccessKey`/`GenerateAWSSecretKey` (IAM credentials), `PromoteSystemImage` (EC2 image domain), and `GenerateCACert`/`GenerateSignedCert` (Spinifex certificate issuance). A later consumer check found the certificate functions have no production consumer outside `operator/cli` (the NATS role uses them only in `nats_test.go`, and `handlers/rds/servingcert.go` only mentions them in a comment), so only `DefaultAccountID`, the key generators and `PromoteSystemImage` still block. +- `admin` (remaining): the operator-only code moved to `operator/admin`. What remains has non-operator consumers and awaits owners: `DefaultAccountID`/`DefaultAccountName` (IAM/account bootstrap), `GenerateAWSAccessKey`/`GenerateAWSSecretKey` (IAM credentials) and `PromoteSystemImage` (the existing `domains/ec2/image`). - `domains/dns`: imports `runtime/compute/vm` (domain-to-runtime), `domains/network/reconcile` for the reconcile lock and `bootstrap/config` for node configuration. - ACM: the IAM AES-GCM helper import (ACM relocation residual). - `runtime/roles/awsgw`: the quota reconcile loop belongs to `domains/admission/quota`, and the EKS token-verify responder belongs with STS or EKS identity. @@ -433,7 +434,6 @@ Enforcement: the owner decided the layering test is blocking now, with a ratchet - `domains/admission/quota` → `domains/ec2/instancetypes`: `reconcile.go`, `records.go` and `vcpu.go` read `DefaultVCPUs` to size instances without a recorded vCPU count. This clears when quota receives vCPU sizing through a consumer-owned capability or an EC2 projection. - IAM implementation consumers in STS, Ochre and ELBv2: `handlers/sts` imports `handlers/iam` across five files (`IAMService`, `SessionRevoker`, `EncryptSecret`/`DecryptSecret`, `AccessKeyStatusActive`, `ValidateTrustPolicyDocument`, `PolicyEffectAllow`/`PolicyEffectDeny`, `IAMAccountBucketName`, `OIDCProviderARN`, `OIDCProviderKey`), not only `oidc_jwks.go`; `gateway/sts/GetCallerIdentity.go` takes `IAMService`; `gateway/bedrock/credentials.go` uses `EncryptSecret`/`DecryptSecret`; and `handlers/elbv2/{lb_role,service_impl}.go` use `EnsureSystemInstanceProfile` and `SystemInstanceRoleEnsurer`. Each clears when its consumer receives a narrow IAM capability, or a shared crypto boundary for the secret helpers, rather than importing the IAM implementation. - `handlers/elbv2` (cross-domain): `service_impl.go` imports `domains/acm` (`Store`, `NewStore`), `domains/dns` (`ELBChanges`, `ELBName`, `Change`, `ActionUpsert`, `ResolveBaseDomain`), `domains/ec2/vpc` (`VPCServiceImpl`) and `domains/network/topology` (`Port`), and `launcher.go` re-exports `domains/ec2/systeminstance` launch types as aliases. These clear when ELBv2 is extracted and each edge becomes a consumer-owned capability, which ADR-0001 sequences after the ELBv2 data-plane boundary is settled. -- `operator/installer/ui` → `admin`: `ui.go` calls `admin.ValidateEmail` for installer input. This clears with the `admin` → `operator/admin` blocker, once `ValidateEmail` has an owner. - `docs/service-interfaces.yaml` (coverage gap): no `ecs.*` subjects are listed and the manifest lint does not cover them, so ECS publisher/subscriber edges play no part in suite selection. Add them only after tracing the actual publishers and subscribers. The layering test enforcing these rules (`architecture/layering`, a module-root repository governance test, not Spinifex product code; run by `make test`, `test-cover` and `test-race`) is blocking, with a ratchet: the debt recorded in this section is narrowly allow-listed by owner and removal condition, and any new, unrecorded forbidden edge fails local preflight and CI. The allow-list mirrors the edges in this section that break an import rule; same-domain legacy edges, placement residuals and security notes are not import-rule violations and have no allow-list entry. @@ -507,7 +507,7 @@ series above; "needs decision" is open. | `utils` | `vpcd_event.go`, `encryption.go` (`ec2_subject.go` moved to `contracts/ec2/v1` as `EnsureDefaultVpcSubject`; its request and reply bodies are still anonymous structs on each side). | Decided: `vpcd_event.go` is a serialised cross-process contract (its `natEvent` payload crosses a NATS request-reply boundary on `vpc.add-nat`/`vpc.delete-nat`), so `domains/network` owns its meaning and its versioned wire form goes under `contracts/network/v1`. `ec2_subject.go` (`SubjectEnsureDefaultVpc`, a NATS request/reply subject between the gateway and the daemon) has `domains/ec2` as its subject-semantics owner, with `contracts/ec2/v1` as its eventual home since it crosses a process boundary. `encryption.go` (the Viperblock master-key loader) joins the Viperblock adapter at `providers/ebs/viperblock`, not generic EBS or foundation code. Blocked on a capability decision: its only callers are `domains/ec2/{image,instance,volume}`, which test the key only to derive the AWS-visible `Encrypted` posture; moving it would make EC2 import a Viperblock-specific provider. It needs an encryption-posture field in the `providers/ebs` contract or an EC2-owned configuration predicate first. | | `vpcd` | `imds_instance_state.go` reads `daemon.LocalState`, `ReadLocalState`, `LocalStatePath`, `NewJetStreamManager` and `InstanceStateBucket` directly (recorded above). | Needs a daemon-owned local-state capability or contract vpcd can consume instead of the `daemon` package. | | `daemon` | Reserved as `runtime/daemon`'s home only; not split or moved. | Deferred: Q-28's write-side consolidation is a prerequisite ADR-0001 names explicitly. | -| `admin` | Operator use cases (`email.go`, `telemetry.go`, `network_detect.go`, `image_import.go`, `images_remove.go`, `orphan_volumes.go`, most of `admin.go` including certificate issuance) consumed only by `operator/cli` and `operator/installer/ui`; plus `DefaultAccountID`/`DefaultAccountName` (consumed by `daemon`, `gateway`, `runtime/roles/awsgw`), `GenerateAWSAccessKey`/`GenerateAWSSecretKey` (`handlers/iam`) and `images_promote.go` (`daemon`, `gateway/spx`). | Partially movable: a read-only assessment found the operator-only files can move to `operator/admin` with no new forbidden edge, cycle or shim (it clears the `operator/installer/ui` → `admin` entry); `images_promote_test.go` shares fixtures with `images_remove_test.go`. Remaining pieces are deferred to their owners: IAM/account bootstrap, IAM credentials, and the existing `domains/ec2/image`. | +| `admin` | `DefaultAccountID`/`DefaultAccountName` (consumed by `daemon`, `gateway`, `runtime/roles/awsgw`, `operator/cli`), `GenerateAWSAccessKey`/`GenerateAWSSecretKey` (`handlers/iam`, `operator/cli`) and `images_promote.go` (`daemon`, `gateway/spx`, `operator/cli`). The operator use cases moved to `operator/admin`. | Deferred to their owners: IAM/account bootstrap, IAM credentials, and the existing `domains/ec2/image`. | | `accountteardown` | Direct calls into `domains/ec2/awsapi/*` action adapters (recorded above). | Deferred: the ADR's `accountteardown` row replaces this with domain-owned cleanup capabilities. | | `lbagent` | The whole package; `handlers/elbv2` imports it as a domain-to-agent implementation edge (recorded above). | Deferred: Q-30 reserves `agents/elbv2` as its destination but forbids moving it as an isolated rename ahead of the common-agent design. | | `migrate` | Nothing at the old path. | Split, not an exact ADR row: ADR-0001 maps `migrate` to `foundation/state/migrate`, but the package held two responsibilities. The KV migration engine and its tests are in `foundation/state/migrate` (`8cd9cff75`); the config-file and object-store upgrade workflow moved to `operator/upgrade` (`919d5bfa6`) by owner decision. Needs an ADR-0001 map clarification before this row can be claimed as target conformance. |