Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a4ec7db
feat(pep): add structured policy audit logging
Jul 12, 2026
85a4958
Merge pull request #116 from ArdurAI/gnanirahulnutakki/feat/p1-policy…
gnanirahulnutakki Jul 12, 2026
f8e288e
docs(release): prevent durable branch deletion
Jul 12, 2026
f421ad8
Merge pull request #118 from ArdurAI/gnanirahulnutakki/chore/release-…
gnanirahulnutakki Jul 12, 2026
44ae088
feat(e10): add bounded self-observability metrics
Jul 13, 2026
cdb8460
Merge pull request #120 from ArdurAI/gnanirahulnutakki/feat/p1-observ…
gnanirahulnutakki Jul 13, 2026
8b56a04
fix(security): pin secure Go toolchain
Jul 13, 2026
4dc1066
chore(deps): bump actions/checkout in the actions group
dependabot[bot] Jul 13, 2026
df8bd0b
Merge pull request #122 from ArdurAI/dependabot/github_actions/dev/ac…
gnanirahulnutakki Jul 14, 2026
def40dc
Merge pull request #121 from ArdurAI/gnanirahulnutakki/chore/sith-sec…
gnanirahulnutakki Jul 14, 2026
fef959f
feat(e2): add direct ClusterProxy snapshot transport
Jul 14, 2026
fc8ca6e
Merge pull request #124 from ArdurAI/gnanirahulnutakki/feat/e2-direct…
gnanirahulnutakki Jul 14, 2026
75499b1
feat(hub): compose direct OCM runtime
Jul 14, 2026
831d068
Merge pull request #126 from ArdurAI/gnanirahulnutakki/feat/e2-hub-di…
gnanirahulnutakki Jul 14, 2026
096dca9
feat(e2): add immutable image digest search
Jul 14, 2026
ce01a4f
Merge pull request #128 from ArdurAI/gnanirahulnutakki/feat/e2-image-…
gnanirahulnutakki Jul 14, 2026
d29c8da
feat(e9): add isolated hub migration command
Jul 14, 2026
a9922a4
Merge pull request #130 from ArdurAI/gnanirahulnutakki/feat/e9-hub-mi…
gnanirahulnutakki Jul 14, 2026
9e75a13
feat(e9): add immutable OCI image contract
Jul 14, 2026
401431b
Merge pull request #132 from ArdurAI/gnanirahulnutakki/feat/e9-oci-image
gnanirahulnutakki Jul 14, 2026
658336f
feat(e9): add fail-closed Helm hub chart
Jul 14, 2026
741a5e5
Merge pull request #134 from ArdurAI/gnanirahulnutakki/feat/e9-helm-c…
gnanirahulnutakki Jul 14, 2026
92bedb3
feat(e9): add fail-closed hub resource profiles
Jul 14, 2026
710a428
Merge pull request #136 from ArdurAI/gnanirahulnutakki/feat/e9-chart-…
gnanirahulnutakki Jul 14, 2026
1ecfb56
feat(e10): add sanitized local trace context (#138)
gnanirahulnutakki Jul 14, 2026
c9e3737
feat(e10): add sanitized auth refusal logs
Jul 14, 2026
ba102f4
Merge pull request #141 from ArdurAI/gnanirahulnutakki/feat/e10-auth-…
gnanirahulnutakki Jul 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ env:
GOLANGCI_VERSION: "v2.12.2"
GOVULNCHECK_VERSION: "v1.6.0"
GORELEASER_VERSION: "v2.17.0"
HELM_VERSION: "v4.2.2"
HELM_LINUX_AMD64_SHA256: "9adafecab4d406853bba163a70e9f104f47dbbf65ce24b7653bae7e36150bcb6"
SYFT_VERSION: "v1.46.0"

jobs:
build-test-lint:
name: build · vet · gofmt · lint · test · e2e
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

Expand Down Expand Up @@ -82,6 +84,22 @@ jobs:
- name: Binary integration smoke test
run: go test -race -count=1 -tags=e2e ./tests/e2e

- name: Immutable OCI image contract
run: make e2e-oci

- name: Install pinned Helm
run: |
set -euo pipefail
archive="$RUNNER_TEMP/helm.tar.gz"
curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 --retry 3 --retry-all-errors \
--output "$archive" "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz"
echo "${HELM_LINUX_AMD64_SHA256} ${archive}" | sha256sum --check --status
tar -xzf "$archive" -C "$RUNNER_TEMP"
install -m 0755 "$RUNNER_TEMP/linux-amd64/helm" "$RUNNER_TEMP/helm"

- name: Fail-closed Helm hub chart contract
run: make e2e-helm HELM="$RUNNER_TEMP/helm"

- name: Install pinned kind
run: go install sigs.k8s.io/kind@v0.32.0

Expand All @@ -92,7 +110,7 @@ jobs:
name: reproducible archives · SPDX SBOM · Homebrew formula
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
id-token: write
attestations: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
Expand Down
13 changes: 13 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: Apache-2.0

# The manifest-list digest is intentionally pinned so every supported Linux architecture resolves
# to the reviewed distroless static runtime, never a floating tag.
FROM gcr.io/distroless/static-debian12@sha256:b7bb25d9f7c31d2bdd1982feb4dafcaf137703c7075dbe2febb41c24212b946f

ARG TARGETARCH

# The build context contains only a static Linux binary assembled by the test or release tooling.
COPY --chown=65532:65532 --chmod=0555 bin/linux/${TARGETARCH}/sith /usr/local/bin/sith

USER 65532:65532
ENTRYPOINT ["/usr/local/bin/sith"]
38 changes: 35 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ BIN_DIR := bin
GOLANGCI ?= golangci-lint
GOVULNCHECK ?= govulncheck
KIND ?= kind
HELM ?= helm
GORELEASER ?= goreleaser
DOCKER ?= docker
KUBECTL ?= kubectl
OCM_SCRATCH_ROOT ?= /Volumes/EXTENDED/tmp/sith-m0
OCM_PREFIX ?= sith-m0
Comment on lines +11 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Non-portable hardcoded default for OCM_SCRATCH_ROOT.

/Volumes/EXTENDED/tmp/sith-m0 is a macOS external-volume path specific to one developer's machine. As the checked-in default for e2e-ocm, this will fail to create/use scratch space on CI runners and any other contributor's machine unless explicitly overridden every time.

💡 Suggested fix: use a portable default
-OCM_SCRATCH_ROOT ?= /Volumes/EXTENDED/tmp/sith-m0
+OCM_SCRATCH_ROOT ?= $(shell mktemp -d /tmp/sith-m0.XXXXXX 2>/dev/null || echo /tmp/sith-m0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
HELM ?= helm
GORELEASER ?= goreleaser
DOCKER ?= docker
KUBECTL ?= kubectl
OCM_SCRATCH_ROOT ?= /Volumes/EXTENDED/tmp/sith-m0
OCM_PREFIX ?= sith-m0
HELM ?= helm
GORELEASER ?= goreleaser
DOCKER ?= docker
KUBECTL ?= kubectl
OCM_SCRATCH_ROOT ?= $(shell mktemp -d /tmp/sith-m0.XXXXXX 2>/dev/null || echo /tmp/sith-m0)
OCM_PREFIX ?= sith-m0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 11 - 16, Replace the machine-specific default value of
OCM_SCRATCH_ROOT in the Makefile with a portable scratch-directory path suitable
for CI and other developer environments, while preserving OCM_PREFIX and the
ability to override the variable externally.


KIND_NODE_IMAGE ?= kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5
POSTGRES_IMAGE ?= postgres:18.4-alpine3.23@sha256:996d0920e4ff9df1fc19dacb904492f3c1ec0ec1cc338f0ad7123be7731c5f5e
Expand All @@ -25,7 +29,7 @@ LDFLAGS := -s -w \
-X $(PKG)/internal/buildinfo.Commit=$(COMMIT) \
-X $(PKG)/internal/buildinfo.Date=$(DATE)

.PHONY: all build test test-scripts perf e2e e2e-kind e2e-postgres e2e-isolation lint vuln fmt fmt-check vet tidy clean run ci release-check help
.PHONY: all build test test-scripts perf e2e e2e-helm e2e-oci e2e-kind e2e-ocm e2e-postgres e2e-isolation lint vuln fmt fmt-check vet tidy clean run ci release-check help

all: build

Expand All @@ -45,9 +49,37 @@ perf: ## Enforce the warm-cache TUI p95 latency budget without race overhead
e2e: ## Build and exercise the real binary as a subprocess
go test -race -count=1 -tags=e2e ./tests/e2e

e2e-helm: ## Validate the fail-closed Helm hub chart with the pinned Helm CLI
HELM_BIN="$(HELM)" go test -race -count=1 -timeout=5m -tags='e2e helm' -run '^TestHelmHubChartContract$$' ./tests/e2e

e2e-oci: ## Build and inspect the local immutable OCI image contract for linux/amd64 and linux/arm64
go test -race -count=1 -timeout=10m -tags='e2e oci' -run '^Test(OCIImageCrossPlatformContract|ContainerfileInstructionGuard)$$' ./tests/e2e

e2e-kind: ## Exercise adapter and binary against two real kind clusters
KIND_BIN="$(KIND)" KIND_NODE_IMAGE="$(KIND_NODE_IMAGE)" \
go test -race -count=1 -timeout=15m -tags='e2e kind' -run '^TestKindFleetFanout$$' ./tests/e2e
go test -race -count=1 -timeout=15m -tags='e2e kind' -run '^Test(KindFleetFanout|KindOCIImageContract)$$' ./tests/e2e

e2e-ocm: ## Prove direct ClusterProxy transport in the pinned two-spoke M0 lab
@set -euo pipefail; \
run_required_e2e_test() { \
local test_name="$$1"; shift; \
local output; \
if ! output="$$("$$@" 2>&1)"; then \
printf '%s\n' "$$output" >&2; return 1; \
fi; \
printf '%s\n' "$$output"; \
grep -Fq -- "--- PASS: $${test_name}" <<<"$$output" || { \
echo "required M0 test $${test_name} did not run" >&2; return 1; \
}; \
}; \
trap 'KIND_BIN="$(KIND)" SITH_M0_SCRATCH_ROOT="$(OCM_SCRATCH_ROOT)" SITH_M0_PREFIX="$(OCM_PREFIX)" hack/experiments/m0-ocm-falsification.sh cleanup' EXIT; \
KIND_BIN="$(KIND)" SITH_M0_SCRATCH_ROOT="$(OCM_SCRATCH_ROOT)" SITH_M0_PREFIX="$(OCM_PREFIX)" SITH_M0_KEEP_CLUSTERS=1 \
hack/experiments/m0-ocm-falsification.sh run; \
export KUBECTL_BIN="$(KUBECTL)" SITH_OCM_HUB_KUBECONFIG="$(OCM_SCRATCH_ROOT)/kubeconfig" SITH_OCM_HUB_CONTEXT="kind-$(OCM_PREFIX)-hub"; \
run_required_e2e_test TestDirectClusterProxyM0 \
go test -v -race -count=1 -timeout=8m -tags='e2e ocm' -run '^TestDirectClusterProxyM0$$' ./internal/hubocm; \
run_required_e2e_test TestHubRuntimeDirectClusterProxyM0 \
go test -v -race -count=1 -timeout=8m -tags='e2e ocm' -run '^TestHubRuntimeDirectClusterProxyM0$$' ./internal/hubruntime

e2e-postgres: ## Prove forced RLS against a temporary digest-pinned PostgreSQL container
DOCKER_BIN="$(DOCKER)" POSTGRES_IMAGE="$(POSTGRES_IMAGE)" \
Expand Down Expand Up @@ -92,7 +124,7 @@ ci: fmt-check vet lint vuln test test-scripts perf e2e build ## Run the full CI
release-check: ## Build and verify the reproducible multi-platform release snapshot twice
@command -v "$(GORELEASER)" >/dev/null || { echo "goreleaser is required" >&2; exit 1; }
@command -v syft >/dev/null || { echo "syft is required" >&2; exit 1; }
@tmp="$$(mktemp -d)"; trap 'rm -rf "$$tmp"' EXIT; \
@set -e; tmp="$$(mktemp -d)"; trap 'rm -rf "$$tmp"' EXIT; \
go mod download; \
go mod verify; \
"$(GORELEASER)" check .goreleaser.yaml; \
Expand Down
105 changes: 97 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ revoked immediately. Exchange responses, including generic failures, are non-cac
handler includes a bounded per-process attempt limiter; a replicated hub must additionally enforce
a shared limit at its ingress or gateway. Deployments must provide the HMAC pepper and Ed25519
private key through a secret manager, keep both out of logs and configuration repositories, and
rotate them under an explicit operational procedure. These are E1 library and HTTP boundaries;
the `sith hub` runtime remains staged behind later hub epics rather than exposing an incomplete
service.
rotate them under an explicit operational procedure. These are E1 library and HTTP boundaries.
The P1 `sith hub` runtime now mounts only the session-authenticated fleet read/refresh surface
below; API-key, OIDC, and cloud-proof exchange handlers remain intentionally unmounted until their
ingress and operator lifecycle are composed.

Pinned OIDC federation uses the same exchange model. Each endpoint is fixed to one requested
workspace, and each provider configuration allowlists an exact HTTPS issuer, audience, token type,
Expand Down Expand Up @@ -159,13 +160,100 @@ only the workspace boundary and registered managed-cluster reference; it never r
kubeconfig, endpoint, or token through the Sith collector contract. Only normalized `inventory`
and `health` facts are accepted, source-stamped, freshness-bounded, and stored behind forced RLS.
Failed refreshes retain the last snapshot as explicitly stale evidence and record only a closed
failure category. The concrete OCM ClusterGateway transport is deliberately not exposed by the
`sith hub` stub until its projected-token lifecycle is wired and exercised as a product adapter.
The same model now answers a read-only, exact cross-cluster correlation such as “every deployment
failure category. The pinned direct OCM ClusterProxy adapter reads the exact rotating
`sith-reader` managed-serviceaccount Secret for a registered spoke, opens a short-lived
Konnectivity tunnel only to that spoke, and verifies both proxy mTLS and the spoke Kubernetes
certificate; it never forwards a caller `Authorization` header, stores a credential, disables
TLS verification, lists or watches Secrets, or carries raw Kubernetes objects across the
collector seam. Its executable two-spoke M0 gate is `make e2e-ocm`, which now also drives a
signed-session request through the TLS hub runtime across both spokes. The same model now answers
a read-only, exact cross-cluster correlation such as “every deployment
named `payments` that is not Healthy” within one workspace. Matching is by exact kind/name/namespace
rather than a prefix, and every answer retains full stale/unreachable coverage rather than claiming
rather than a prefix. Every returned cluster and matching fact retains its source identity and
observation time, and every answer retains full stale/unreachable coverage rather than claiming
that a partial fleet is complete.

### Governed hub runtime (P1)

`sith hub` is an in-cluster, TLS-only process. It has no listener default and exits non-zero before
opening a listener, database pool, or Kubernetes client unless all of these deployment inputs are
present and valid:

- `SITH_HUB_LISTEN_ADDR` and `SITH_HUB_DATABASE_URL` (the database must use the existing
non-owner, forced-RLS application role and TLS);
- `SITH_HUB_SESSION_ISSUER`, `SITH_HUB_SESSION_AUDIENCE`, `SITH_HUB_SESSION_KEY_ID`, and
`SITH_HUB_SESSION_PUBLIC_KEY_FILE` (a static Ed25519 PKIX public key; no remote discovery);
- `SITH_HUB_SERVER_TLS_CERT_FILE` and `SITH_HUB_SERVER_TLS_KEY_FILE` for the hub HTTPS listener;
- `SITH_HUB_PROXY_ADDRESS`, `SITH_HUB_PROXY_SERVER_NAME`, `SITH_HUB_PROXY_CA_FILE`,
`SITH_HUB_PROXY_CERT_FILE`, `SITH_HUB_PROXY_KEY_FILE`, and `SITH_HUB_KUBE_API_SERVER_NAME` for
the direct ClusterProxy mTLS path.

Every referenced key, certificate, or CA file must be a read-only regular file from a deployment
mount. The runtime obtains its Kubernetes identity only with in-cluster configuration; it has no
kubeconfig fallback and uses that identity through the fixed `sith-reader` Secret reader. It serves
only `POST /v1/workspaces/{workspace}/fleet:refresh`,
`GET /v1/workspaces/{workspace}/fleet`, and
`GET /v1/workspaces/{workspace}/fleet/images/{sha256:<64-lowercase-hex>}`. Every route requires an
exact signed Sith session, derives the workspace scope from its signed memberships, carries that
scope through the PEP and RLS seams, accepts no query parameters, and returns only normalized
coverage/fleet data under `Cache-Control: no-store`.

After deriving the signed scope, the hub mints one opaque local trace ID for the governed request.
It strips common caller-supplied trace and correlation carriers, never echoes or forwards them,
and carries the local ID through the PEP audit record and each snapshot transport attempt. The hub
logs only local trace ID, fixed stage, fixed outcome, and bounded duration; it records no workspace,
actor, spoke, endpoint, resource, selector, argument digest, credential, raw error, or returned
data in trace events. This is not a telemetry exporter: it adds no OpenTelemetry SDK, listener,
network egress, queue, trace store, persistence, or action-intent protocol.

Before a signed scope exists, the authentication gate emits one local WARN record for every
refusal with only the fixed `hub-auth` surface and `refused` outcome. It deliberately does not
distinguish credential failure modes or carry a trace/correlation ID, token, header, path, client
address, workspace, principal, or verifier error. The record is a passive alerting signal, not an
audit record, rate limiter, telemetry export, or additional authentication decision.

The image route answers one exact, immutable runtime digest question across registered spokes. The
direct reader accepts only canonical digests normalized from ordinary
`Pod.Status.ContainerStatuses[].ImageID`; PodSpec image strings, init and ephemeral container
statuses, mutable tags, malformed values, and ambiguous runtime IDs abstain. Sith makes no registry
request, image pull, SBOM retrieval, vulnerability-feed lookup, or credential use for this read.
The result remains coverage-honest: matching Pod inventory facts retain source and freshness, and
unreachable or stale spokes are reported rather than assumed clean.

### Hub schema migration

Run `sith hub migrate` as a short-lived deployment Job before starting `sith hub`. It accepts only
`SITH_HUB_MIGRATION_OWNER_DATABASE_URL` and `SITH_HUB_APPLICATION_DATABASE_ROLE`; mount the owner
database URL from the deployment secret provider and set the application role explicitly. The
command requires TLS for any non-local database target, applies the checksum-locked serializable
migration ledger, audits forced RLS, attempts to close its one owner connection, and exits. It never opens the
hub listener, creates a Kubernetes client, or starts collection.

The normal hub process continues to use only `SITH_HUB_DATABASE_URL` for the non-owner application
role. Do not reuse the migration-owner credential in the hub Deployment or place either database
URL, certificates, tokens, or private keys in chart values or logs.

### OCI image deployment contract

The hub OCI recipe uses the digest-pinned distroless static Debian 12 runtime and contains only a
static Linux Sith binary running as UID/GID `65532`. It has no shell, package manager, default
configuration, Kubernetes credential, certificate, database URL, or secret. The source test builds
and inspects both `linux/amd64` and `linux/arm64` variants without publishing; the native image must
also run with a read-only filesystem, no network, no Linux capabilities, and no privilege
escalation, then complete the same contract as a hardened Job on each of two Kind clusters.
The no-network setting applies only to those isolated image checks. A deployed hub needs narrowly
allowlisted egress to its configured runtime dependencies, including its database and, when
enabled, the pinned OIDC discovery and JWKS endpoints.

This is not a published image reference. The fail-closed [`charts/sith-hub`](charts/sith-hub)
chart requires an explicit immutable `repository@sha256:...` image reference and refuses tags,
especially `latest`; it invokes `sith hub migrate` in a separate short-lived Job before the
non-owner hub Deployment starts. Its defaults intentionally cannot install until a release-bound
hub image and operator-provided Secret references exist; it never renders secret material. The
chart permits only fixed `light` and `heavy` resource profiles, which retain identical security,
credential, and RBAC controls. This first F9.3a profile slice does not claim a public image,
in-chart database, or HA; those parent-F9.3 topology and custody capabilities need later evidence.

`sith serve --mcp` exposes `fleet.inventory`, `fleet.health`, `fleet.correlate`, and
`fleet.cve-search` over MCP Streamable HTTP. All four tools are cache-only and carry
`readOnlyHint:true`; they use the exact workspace-required query path used by the CLI, TUI, and web
Expand Down Expand Up @@ -260,7 +348,8 @@ make release-check
The gate also compiles the binary under a functional HTTP/HTTPS egress sentinel and exercises
local commands, deterministic investigation, plus the running web UI and MCP server with an official SDK client. A source
boundary exact-allowlists production network, filesystem-write, and subprocess imports, confines
client-go transport to the kubeconfig adapter, and rejects known telemetry SDKs and low-level
local-mode client-go transport to the kubeconfig adapter, permits only the separately reviewed
tenant-scoped direct OCM adapter in governed mode, and rejects known telemetry SDKs and low-level
network bypasses. Together these checks prove the reviewed paths; they are regression controls
rather than an operating-system network sandbox.

Expand Down
6 changes: 6 additions & 0 deletions charts/sith-hub/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: sith-hub
description: Fail-closed deployment contract for the Sith governed fleet hub
type: application
version: 0.2.0
appVersion: "unpublished"
32 changes: 32 additions & 0 deletions charts/sith-hub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Sith hub Helm chart

This chart is a fail-closed deployment contract for a released Sith hub image. The repository does not currently publish that image, so the chart has deliberately invalid default values and cannot be installed until an operator supplies an immutable `repository@sha256:<64 lowercase hex>` reference. Tags, including `latest`, are rejected by both the value schema and template logic.

This `F9.3a` slice provides only fixed resource envelopes. It does not claim the parent F9.3 end state of a minimal in-chart Postgres for light or an HA hub with external Postgres/cloud KMS for heavy: those need separate E3 custody and topology evidence before they can be rendered safely.

The chart creates no `Secret`, `data`, or `stringData` block. An E3-approved KMS/ExternalSecret materializer must create these existing Secret objects before an installation:

| Value | Required Secret keys | Consumer |
| --- | --- | --- |
| `runtime.existingSecret` | `database-url`, `session-public.pem`, `server-tls.crt`, `server-tls.key`, `proxy-ca.crt`, `proxy-tls.crt`, `proxy-tls.key` | long-running `sith hub` Deployment |
| `migration.existingSecret` | `owner-database-url` | short-lived `sith hub migrate` hook Job |

`migration.applicationRole` is a non-secret PostgreSQL role name. The migration hook runs before install and upgrade, blocks the release if it fails, and receives no Kubernetes service-account token or runtime TLS material. The Deployment receives an in-cluster token only to read the fixed `sith-reader` managed-serviceaccount Secret; its ClusterRole permits exactly `get` on that one resource name and no list/watch or write verbs.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid rendering the issue reference as a heading.

This line starts with #103, triggering MD018 and rendering unexpectedly. Prefix the references with prose.

Proposed fix
-#103/#104 route remains out of scope and blocked pending an official upstream release.
+issues `#103` and `#104` remain out of scope and blocked pending an official upstream release.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@charts/sith-hub/README.md` at line 15, Update the issue reference line in the
README so it no longer begins with `#103`; prefix the reference with descriptive
prose while preserving the issue link or reference content.

Source: Linters/SAST tools

The chart permits exactly two fixed profiles for both the hub and its migration hook:

| Profile | Requests | Limits | Intended envelope |
| --- | --- | --- | --- |
| `light` | 100m CPU, 128Mi memory | 500m CPU, 512Mi memory | development and lab scheduling envelope |
| `heavy` | 500m CPU, 512Mi memory | 2 CPU, 2Gi memory | larger production-like scheduling envelope |

The heavy profile reserves five times the requested CPU and four times the requested memory, so it carries a correspondingly higher node-pool cost. These are fixed scheduling bounds, not measured capacity claims; no arbitrary resource override or third profile is accepted. Both profiles use the same immutable image requirement, existing Secret references, migration isolation, RBAC, probes, and pod/container hardening. They do not change replica count, database custody, or KMS materialization, so `heavy` does not claim unproven high availability.

The chart pins workload hardening (UID/GID 65532, read-only root filesystem, RuntimeDefault seccomp, no privilege escalation, and all Linux capabilities dropped). It deliberately does not create a broad egress NetworkPolicy: the database and pinned OCM endpoints are deployment-specific, so operators must place the release in a namespace with an appropriate least-privilege egress policy. KMS provider resources, release-bound image publication, real install/upgrade proof, air-gap bundles, and addon packaging are later E9/E3 slices.

Validate supplied values before applying anything:

```bash
helm lint charts/sith-hub -f operator-values.yaml
helm template sith-hub charts/sith-hub --namespace sith-system -f operator-values.yaml
```
Loading