Skip to content

Commit 3d5e32b

Browse files
committed
fix(runtime): resolve F-08 harden deployment handoff
1 parent 293a4ac commit 3d5e32b

6 files changed

Lines changed: 51 additions & 9 deletions

File tree

.dockerignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
.git
22
.github
33
.planning
4+
.env
5+
.coverage
46
.venv
57
__pycache__
68
.pytest_cache
9+
.mypy_cache
710
.ruff_cache
11+
*.py[cod]
812
tests
913
docs
10-

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ FROM python:3.12-slim AS runtime
22

33
ENV PYTHONDONTWRITEBYTECODE=1 \
44
PYTHONUNBUFFERED=1 \
5+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
56
PORT=8080
67

78
WORKDIR /app
@@ -10,12 +11,12 @@ RUN addgroup --system app && adduser --system --ingroup app app
1011

1112
COPY pyproject.toml README.md ./
1213
COPY src ./src
13-
RUN pip install --no-cache-dir .
14+
RUN pip install --no-cache-dir --no-compile .
1415

1516
USER app
1617
EXPOSE 8080
18+
STOPSIGNAL SIGTERM
1719
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
1820
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health/live', timeout=2)"
1921

2022
CMD ["uvicorn", "cas_reference_product.app:app", "--host", "0.0.0.0", "--port", "8080"]
21-

deployment/cas-platform.interface.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,3 @@ spec:
2323
- FOUNDRY_AGENT_NAME
2424
deploymentInjected:
2525
- APPLICATIONINSIGHTS_CONNECTION_STRING
26-
outputsConsumed:
27-
- workloadPrincipalId
28-
- applicationInsightsId
29-
- logAnalyticsWorkspaceId
30-

docs/architecture.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ The Foundry call is isolated behind the `WorkflowAgentService` protocol. This ke
2222

2323
## Deployment Interface
2424

25-
`deployment/cas-platform.interface.yaml` records the contract: Linux AMD64 image, port 8080, internal ingress by default, system-assigned identity, probes, non-secret identifiers, and platform outputs. It does not deploy resources.
25+
`deployment/cas-platform.interface.yaml` records the application contract: Linux AMD64 image, port
26+
8080, internal ingress by default, system-assigned identity, probes, and configuration inputs. It does
27+
not deploy resources.
28+
29+
The application consumes only the environment values listed in that interface. Platform resource IDs
30+
and principal IDs remain deployment-orchestration outputs and are not application configuration.
2631

2732
## Observability Boundaries
2833

docs/operations.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99

1010
Submit `examples/prompt-envelope.json` to `POST /api/v1/workflows`. Liveness is `/health/live`; readiness is `/health/ready`.
1111

12+
For a hardened local container run:
13+
14+
```powershell
15+
docker run --rm --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges `
16+
-p 8080:8080 cas-reference-product:local
17+
```
18+
1219
## Foundry Mode
1320

1421
Set `ENVIRONMENT` to `dev`, `test`, or `prod`; set `WORKFLOW_BACKEND=foundry`; provide the non-secret `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_AGENT_NAME`. The Azure-hosted workload uses its system-assigned managed identity. Do not configure API keys or client secrets.
@@ -23,3 +30,7 @@ are disabled; explicit spans do not record prompt or output content.
2330
## Platform Handoff
2431

2532
Build a Linux AMD64 image and pass its immutable image reference to the `containerImage` parameter of `cas-platform`. Review `deployment/cas-platform.interface.yaml` before platform changes. This repository intentionally contains no Azure deployment command.
33+
34+
The Docker build context excludes local `.env` files and development artifacts. The application does
35+
not consume platform resource IDs; deployment orchestration retains those outputs for RBAC and
36+
operations workflows.

tests/test_runtime_contracts.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from pathlib import Path
2+
3+
ROOT = Path(__file__).parents[1]
4+
5+
6+
def test_docker_build_context_excludes_local_configuration() -> None:
7+
ignored = (ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines()
8+
9+
assert ".env" in ignored
10+
assert ".coverage" in ignored
11+
assert ".venv" in ignored
12+
13+
14+
def test_container_runs_non_root_with_graceful_stop() -> None:
15+
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
16+
17+
assert "\nUSER app\n" in dockerfile
18+
assert "\nSTOPSIGNAL SIGTERM\n" in dockerfile
19+
assert "--no-cache-dir --no-compile" in dockerfile
20+
21+
22+
def test_platform_interface_lists_only_application_inputs() -> None:
23+
interface = (ROOT / "deployment" / "cas-platform.interface.yaml").read_text(encoding="utf-8")
24+
25+
assert "outputsConsumed" not in interface
26+
assert "APPLICATIONINSIGHTS_CONNECTION_STRING" in interface
27+
assert "targetPort: 8080" in interface

0 commit comments

Comments
 (0)