Implement the engine spine: claim, outbox, machine, API
Contracts first (T02–T04): approval claim schema with issuer, freshness,
and binding digest; local transactional outbox wire; load-bearing cadence
as heartbeat or reconciliation (layer.yaml declared).
Then the object (T06–T08): SQLite closed state machine, CAS supersession,
distinct-approver fail-closed, revocation without holder cooperation,
outbox insert in the same transaction. Tests fail the mutation when
emission fails, and revoke while the drain sink is down.
Introspection GET /v1/approvals/{id}/claim is a PIP fact, not a decision.
No public consume (T05 waits on GH-WP-0002-T06). Canon T-06 coverage for
wrong binding, expiry, revoke, and supersede.
FLEX-WP-0017 T03 is unblocked on this object; T05 remains blocked only on
consumption ordering.
Assistant: grok
Assistant-Session: 01a04ceb-2057-7e20-b0f9-c282964d5dd9
2026-08-29 12:52:49 +02:00
|
|
|
SHELL := /usr/bin/env bash
|
|
|
|
|
.DEFAULT_GOAL := test
|
|
|
|
|
|
Adopt Alpine as the sanctioned base; release candidate scans clean
Operator adopted Alpine/musl as the base and trivy as the scanner.
Containerfile.alpine is promoted to Containerfile and the Debian slim variant
is retired rather than kept as an option -- it shipped three perl-base
CRITICALs with no upstream fix, in a package this service never invokes.
Promoting Alpine left 7 HIGH and 1 MEDIUM, all libuuid 2.42.1-r0 as shipped by
the pinned Alpine 3.24.1, and all with fixes in 2.42.3. The runtime stage now
requires libuuid>=2.42.3-r1. That is a version floor, not a floating upgrade:
the base stays digest-pinned and reproducible, and a vulnerable libuuid fails
the build instead of shipping.
The candidate scans 0 CRITICAL / 0 HIGH / 0 MEDIUM / 0 LOW. Verified on that
exact artifact: non-root uid 10001, pip absent, schema v3, tenant default
tenant:platform, fresh-store migrate and verify clean, restart persistence via
re-verify on the same volume, both production fail-closed refusals, 111 tests
on musl, kubectl dry-run passing.
The gate is now reproducible instead of a one-off. make image-scan fails on any
CRITICAL or HIGH, and make image-release runs build then scan then push, so a
failing scan blocks the push by construction rather than by whoever remembers
to look.
Not released. docker push was attempted and refused by this session's sandbox
as an outward-facing publish, and was not worked around. No release digest
exists, so the manifest deliberately keeps REPLACE_WITH_RELEASE_DIGEST -- it
must be pinned to the registry manifest digest, never the tag and never the
local image id. T03 stays wait on that push plus the still-unmaterialized
KeyCape registrations and audit sender credential.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM5HnEAhokxdfcPqBNpT7D
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715850@bnt-lap001
Assistant-Session: eb557e93-7cb1-45d0-9e57-7d15b3edc60e
2026-09-06 23:01:41 +02:00
|
|
|
IMAGE ?= forgejo.coulomb.social/coulomb/approval-engine
|
|
|
|
|
VERSION ?= 0.1.0
|
|
|
|
|
# trivy is the sanctioned scanner (operator decision, 2026-09-06).
|
|
|
|
|
TRIVY ?= aquasec/trivy:latest
|
|
|
|
|
|
Implement the engine spine: claim, outbox, machine, API
Contracts first (T02–T04): approval claim schema with issuer, freshness,
and binding digest; local transactional outbox wire; load-bearing cadence
as heartbeat or reconciliation (layer.yaml declared).
Then the object (T06–T08): SQLite closed state machine, CAS supersession,
distinct-approver fail-closed, revocation without holder cooperation,
outbox insert in the same transaction. Tests fail the mutation when
emission fails, and revoke while the drain sink is down.
Introspection GET /v1/approvals/{id}/claim is a PIP fact, not a decision.
No public consume (T05 waits on GH-WP-0002-T06). Canon T-06 coverage for
wrong binding, expiry, revoke, and supersede.
FLEX-WP-0017 T03 is unblocked on this object; T05 remains blocked only on
consumption ordering.
Assistant: grok
Assistant-Session: 01a04ceb-2057-7e20-b0f9-c282964d5dd9
2026-08-29 12:52:49 +02:00
|
|
|
test: ## Run unit tests
|
|
|
|
|
python3 -m pytest -q
|
|
|
|
|
|
2026-09-02 00:52:04 +02:00
|
|
|
image-build: ## Build the production image locally
|
Adopt Alpine as the sanctioned base; release candidate scans clean
Operator adopted Alpine/musl as the base and trivy as the scanner.
Containerfile.alpine is promoted to Containerfile and the Debian slim variant
is retired rather than kept as an option -- it shipped three perl-base
CRITICALs with no upstream fix, in a package this service never invokes.
Promoting Alpine left 7 HIGH and 1 MEDIUM, all libuuid 2.42.1-r0 as shipped by
the pinned Alpine 3.24.1, and all with fixes in 2.42.3. The runtime stage now
requires libuuid>=2.42.3-r1. That is a version floor, not a floating upgrade:
the base stays digest-pinned and reproducible, and a vulnerable libuuid fails
the build instead of shipping.
The candidate scans 0 CRITICAL / 0 HIGH / 0 MEDIUM / 0 LOW. Verified on that
exact artifact: non-root uid 10001, pip absent, schema v3, tenant default
tenant:platform, fresh-store migrate and verify clean, restart persistence via
re-verify on the same volume, both production fail-closed refusals, 111 tests
on musl, kubectl dry-run passing.
The gate is now reproducible instead of a one-off. make image-scan fails on any
CRITICAL or HIGH, and make image-release runs build then scan then push, so a
failing scan blocks the push by construction rather than by whoever remembers
to look.
Not released. docker push was attempted and refused by this session's sandbox
as an outward-facing publish, and was not worked around. No release digest
exists, so the manifest deliberately keeps REPLACE_WITH_RELEASE_DIGEST -- it
must be pinned to the registry manifest digest, never the tag and never the
local image id. T03 stays wait on that push plus the still-unmaterialized
KeyCape registrations and audit sender credential.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM5HnEAhokxdfcPqBNpT7D
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715850@bnt-lap001
Assistant-Session: eb557e93-7cb1-45d0-9e57-7d15b3edc60e
2026-09-06 23:01:41 +02:00
|
|
|
docker build -f Containerfile -t approval-engine:local -t $(IMAGE):$(VERSION) .
|
|
|
|
|
|
|
|
|
|
image-scan: ## Scan the built image; fails on any CRITICAL or HIGH finding
|
|
|
|
|
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock $(TRIVY) \
|
|
|
|
|
image --scanners vuln --severity CRITICAL,HIGH \
|
|
|
|
|
--exit-code 1 $(IMAGE):$(VERSION)
|
|
|
|
|
|
|
|
|
|
image-release: image-build image-scan ## Build, scan, then push. Push only runs if the scan passes.
|
|
|
|
|
docker push $(IMAGE):$(VERSION)
|
|
|
|
|
@echo "Pin this digest in deploy/approval-engine.yaml (never the tag):"
|
|
|
|
|
@docker inspect --format '{{index .RepoDigests 0}}' $(IMAGE):$(VERSION)
|
2026-09-02 00:52:04 +02:00
|
|
|
|
|
|
|
|
deploy-dry-run: ## Validate Kubernetes manifests without applying them
|
|
|
|
|
kubectl apply --dry-run=client -f deploy/approval-engine.yaml -f deploy/networkpolicies.yaml
|
|
|
|
|
|
Implement the engine spine: claim, outbox, machine, API
Contracts first (T02–T04): approval claim schema with issuer, freshness,
and binding digest; local transactional outbox wire; load-bearing cadence
as heartbeat or reconciliation (layer.yaml declared).
Then the object (T06–T08): SQLite closed state machine, CAS supersession,
distinct-approver fail-closed, revocation without holder cooperation,
outbox insert in the same transaction. Tests fail the mutation when
emission fails, and revoke while the drain sink is down.
Introspection GET /v1/approvals/{id}/claim is a PIP fact, not a decision.
No public consume (T05 waits on GH-WP-0002-T06). Canon T-06 coverage for
wrong binding, expiry, revoke, and supersede.
FLEX-WP-0017 T03 is unblocked on this object; T05 remains blocked only on
consumption ordering.
Assistant: grok
Assistant-Session: 01a04ceb-2057-7e20-b0f9-c282964d5dd9
2026-08-29 12:52:49 +02:00
|
|
|
help: ## Show this help
|
|
|
|
|
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \
|
|
|
|
|
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-24s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
|
|
|
|
|
|
Adopt Alpine as the sanctioned base; release candidate scans clean
Operator adopted Alpine/musl as the base and trivy as the scanner.
Containerfile.alpine is promoted to Containerfile and the Debian slim variant
is retired rather than kept as an option -- it shipped three perl-base
CRITICALs with no upstream fix, in a package this service never invokes.
Promoting Alpine left 7 HIGH and 1 MEDIUM, all libuuid 2.42.1-r0 as shipped by
the pinned Alpine 3.24.1, and all with fixes in 2.42.3. The runtime stage now
requires libuuid>=2.42.3-r1. That is a version floor, not a floating upgrade:
the base stays digest-pinned and reproducible, and a vulnerable libuuid fails
the build instead of shipping.
The candidate scans 0 CRITICAL / 0 HIGH / 0 MEDIUM / 0 LOW. Verified on that
exact artifact: non-root uid 10001, pip absent, schema v3, tenant default
tenant:platform, fresh-store migrate and verify clean, restart persistence via
re-verify on the same volume, both production fail-closed refusals, 111 tests
on musl, kubectl dry-run passing.
The gate is now reproducible instead of a one-off. make image-scan fails on any
CRITICAL or HIGH, and make image-release runs build then scan then push, so a
failing scan blocks the push by construction rather than by whoever remembers
to look.
Not released. docker push was attempted and refused by this session's sandbox
as an outward-facing publish, and was not worked around. No release digest
exists, so the manifest deliberately keeps REPLACE_WITH_RELEASE_DIGEST -- it
must be pinned to the registry manifest digest, never the tag and never the
local image id. T03 stays wait on that push plus the still-unmaterialized
KeyCape registrations and audit sender credential.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM5HnEAhokxdfcPqBNpT7D
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715850@bnt-lap001
Assistant-Session: eb557e93-7cb1-45d0-9e57-7d15b3edc60e
2026-09-06 23:01:41 +02:00
|
|
|
.PHONY: test image-build image-scan image-release deploy-dry-run help
|