Add an on/off switch for the MAS facility.
MAS-WP-0007 is finished. power.sh scales the MX and statute together and records the power annotation. apply.sh keeps an off facility off. The live facility was annotated power=on and was not restarted. Assistant: grok Assistant-Session: 01a198fa-fd27-7e41-a0a2-57355c3e4efd
This commit is contained in:
parent
b108e89af4
commit
f18eb0b8df
12 changed files with 344 additions and 36 deletions
|
|
@ -687,7 +687,8 @@ multimodal framework itself are out too.
|
|||
3. v0.2 is declared final, or is withdrawn with recorded reasons;
|
||||
4. the handoff reading of P2 is recorded in the v0.2 spec text, and the
|
||||
address spec points to the mode contract;
|
||||
5. the live test environment is retired, with its evidence archived;
|
||||
5. the MAS facility can be turned off and back on, keeping its evidence,
|
||||
credentials, and DNS;
|
||||
6. mode contract v0.1 is published with JSON schemas and fixtures;
|
||||
7. the email mode reference passes them in both roles: as master against
|
||||
the callback stub, and as slave under the stub outside master;
|
||||
|
|
|
|||
27
SCOPE.md
27
SCOPE.md
|
|
@ -58,9 +58,11 @@ delivery stays external.
|
|||
outside master that issues case ids), a scripted callback stub, a stub
|
||||
outside master and the flow runner. Email is the only real channel. See
|
||||
`docs/reuse-surface.md`.
|
||||
- **Live test environment.** `deploy/railiance/`: the MX for
|
||||
- **MAS facility.** `deploy/railiance/`: the MX and statute for
|
||||
`mas.coulomb.social`, with replies through Scaleway TEM capped at 300.
|
||||
It is temporary; MAS-WP-0007 retires it.
|
||||
`power.sh` turns the two Deployments on and off. Off keeps the
|
||||
namespace, evidence volume, credentials, DNS, and LoadBalancer.
|
||||
MAS-WP-0007.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
|
|
@ -85,23 +87,22 @@ delivery stays external.
|
|||
- Status: active. The reference experiment (MAS-WP-0002) passed its
|
||||
decision gate on 2026-10-18; the findings are in
|
||||
`docs/experiment-review.md`.
|
||||
- Finished: MAS-WP-0001 to 0006 and 0008 to 0014. That covers the
|
||||
Postfix profile, the v0.1.1 errata, v0.2 (final), the address composer,
|
||||
the mode contract v0.1 (final) with authentication, one outbound path
|
||||
and bundle-only flows, and the second implementation (`impl/js/`, both
|
||||
specs, both contract sides).
|
||||
- Open: MAS-WP-0007 (retire the live MX; backlog, kept by decision).
|
||||
- Finished: MAS-WP-0001 to 0015. That covers the Postfix profile, the
|
||||
v0.1.1 errata, v0.2 (final), the address composer, the mode contract
|
||||
v0.1 (final) with authentication, one outbound path and bundle-only
|
||||
flows, the second implementation (`impl/js/`, both specs, both contract
|
||||
sides), and the switchable MAS facility.
|
||||
- v0.2 is final (founder, 2026-10-19, after MAS-WP-0009). Mode contract
|
||||
v0.1 is final too (founder, 2026-10-19, after MAS-WP-0014: independent
|
||||
JS email side; MCP-1 to MCP-6).
|
||||
- P2 is decided: `!MODE` requests a handoff (founder, 2026-10-19; SCP-8,
|
||||
v0.2 §7.1). The mode contract and its reference implementation are
|
||||
done (MAS-WP-0010); `mas test` runs the contract corpus.
|
||||
- The live MX runs image `mas-live:v0.2-480e69a` (redeployed 2026-10-19)
|
||||
and is kept up by founder decision (MAS-WP-0007 stays in backlog).
|
||||
The deployment with MAS-WP-0012 (one outbound path) is recorded in
|
||||
MAS-WP-0007.
|
||||
- Tests: `.venv/bin/pytest` runs 274 tests, offline apart from loopback
|
||||
- The MAS facility runs image `mas-live:v0.2-480e69a` (redeployed
|
||||
2026-10-19). Founder decision 2026-11-01: keep it and power it on or
|
||||
off (MAS-WP-0007). It was left on. The 300-reply cap, the side-loaded
|
||||
image, and snakeoil SMTP TLS stay until a later decision.
|
||||
- Tests: `.venv/bin/pytest` runs 280 tests, offline apart from loopback
|
||||
fixtures, the Docker probe and Node for `impl/js/`. `mas test` runs
|
||||
four corpora: parser 64, statute 16, v0.2 statute-aware 33, mode
|
||||
contract 195. `cd impl/js && node --test` runs 85 more.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
| workplan | MAS-WP-0004 | finished | — | workplans/MAS-WP-0004-postfix-integration-profile.md |
|
||||
| workplan | MAS-WP-0005 | finished | — | workplans/MAS-WP-0005-spec-errata-v0-1-1.md |
|
||||
| workplan | MAS-WP-0006 | finished | — | workplans/MAS-WP-0006-address-composer.md |
|
||||
| workplan | MAS-WP-0007 | active | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| workplan | MAS-WP-0007 | finished | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| workplan | MAS-WP-0008 | finished | — | workplans/MAS-WP-0008-finalize-v0-2.md |
|
||||
| workplan | MAS-WP-0009 | finished | — | workplans/MAS-WP-0009-declare-v0-2-final.md |
|
||||
| workplan | MAS-WP-0010 | finished | — | workplans/MAS-WP-0010-mode-reference-implementation.md |
|
||||
|
|
@ -61,9 +61,9 @@
|
|||
| task | MAS-WP-0006-T01 | done | — | workplans/MAS-WP-0006-address-composer.md |
|
||||
| task | MAS-WP-0006-T02 | done | — | workplans/MAS-WP-0006-address-composer.md |
|
||||
| task | MAS-WP-0007-T01 | cancel | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T02 | todo | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T03 | todo | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T04 | todo | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T02 | done | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T03 | done | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0007-T04 | done | — | workplans/MAS-WP-0007-switchable-facility.md |
|
||||
| task | MAS-WP-0008-T01 | done | — | workplans/MAS-WP-0008-finalize-v0-2.md |
|
||||
| task | MAS-WP-0009-T01 | done | — | workplans/MAS-WP-0009-declare-v0-2-final.md |
|
||||
| task | MAS-WP-0009-T02 | done | — | workplans/MAS-WP-0009-declare-v0-2-final.md |
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# MAS live-test MX for mas.coulomb.social (MAS-WP-0003).
|
||||
# MAS facility image for mas.coulomb.social (MAS-WP-0007).
|
||||
# Build from the repo root: docker build -f deploy/railiance/Dockerfile .
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
|
|
|
|||
37
deploy/railiance/README.md
Normal file
37
deploy/railiance/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# MAS facility
|
||||
|
||||
The MX and the statute for `mas.coulomb.social`. Replies go out through
|
||||
Scaleway TEM and stay capped at 300. MAS-WP-0007.
|
||||
|
||||
## On and off
|
||||
|
||||
`power.sh` scales `deploy/mas-mx` and `deploy/mas-statute` together. It
|
||||
writes annotation `mas.coulomb.social/power` on namespace `mas`.
|
||||
|
||||
| Power | What runs | What stays |
|
||||
|---|---|---|
|
||||
| `on` | both Deployments at 1 replica | evidence PVC, Services, Ingress, ExternalSecret, DNS, OpenBao role |
|
||||
| `off` | both Deployments at 0 | the same durable parts; port 25 has no ready pod |
|
||||
|
||||
```bash
|
||||
KUBECONFIG=~/.kube/config-railiance01 deploy/railiance/power.sh status
|
||||
KUBECONFIG=~/.kube/config-railiance01 deploy/railiance/power.sh off
|
||||
KUBECONFIG=~/.kube/config-railiance01 deploy/railiance/power.sh on
|
||||
```
|
||||
|
||||
The workstation's default kubeconfig is `ops-mason-builder`. That identity
|
||||
can see Services and cannot scale these Deployments. Use the railiance01
|
||||
admin kubeconfig for `power.sh` and `apply.sh`.
|
||||
|
||||
`apply.sh IMAGE` renders `manifests.yaml` and applies it. When the
|
||||
annotation is `off`, both Deployments are rendered at 0 replicas, so a
|
||||
later apply does not turn the facility back on. A missing annotation means
|
||||
on. The script stops if it cannot read the namespace.
|
||||
|
||||
## Limits
|
||||
|
||||
One replica. The image is side-loaded into k3s containerd, not pulled from
|
||||
a registry. SMTP TLS is the image's snakeoil certificate. The running image
|
||||
is `docker.io/library/mas-live:v0.2-480e69a`, whose banner still says
|
||||
`MAS live test`. These stay until a later decision. This switch does not
|
||||
roll a new image.
|
||||
23
deploy/railiance/apply.sh
Executable file
23
deploy/railiance/apply.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
# Apply the facility manifest without undoing an off switch (MAS-WP-0007).
|
||||
# A missing power annotation is on. Refuses to apply when the namespace
|
||||
# annotation cannot be read.
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: apply.sh IMAGE" >&2
|
||||
exit 2
|
||||
fi
|
||||
image=$1
|
||||
|
||||
if ! power=$(kubectl get namespace mas \
|
||||
-o jsonpath='{.metadata.annotations.mas\.coulomb\.social/power}'); then
|
||||
echo "cannot read namespace mas; refusing to apply" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$power" ]; then
|
||||
power=on
|
||||
fi
|
||||
|
||||
python3 ./facility.py render "$image" "$power" < manifests.yaml | kubectl apply -f -
|
||||
81
deploy/railiance/facility.py
Normal file
81
deploy/railiance/facility.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""Render the MAS facility manifest for a power state.
|
||||
|
||||
This file does not talk to the cluster. power.sh and apply.sh do.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
SWITCHABLE = ("mas-mx", "mas-statute")
|
||||
|
||||
|
||||
def render(manifest: str, image: str, power: str) -> str:
|
||||
"""Return the manifest with IMAGE filled in and replicas set from power.
|
||||
|
||||
`on` keeps one replica of each switchable Deployment. `off` sets both
|
||||
to zero. Services, the evidence volume, the Ingress, and the
|
||||
ExternalSecret are copied through unchanged.
|
||||
"""
|
||||
if power not in ("on", "off"):
|
||||
raise ValueError(f"power must be on or off, got {power!r}")
|
||||
if not image or any(ch.isspace() for ch in image):
|
||||
raise ValueError("image must be a single token")
|
||||
if "image: IMAGE" not in manifest:
|
||||
raise ValueError("manifest has no image: IMAGE placeholder")
|
||||
|
||||
rendered = manifest.replace("image: IMAGE", f"image: {image}")
|
||||
docs = rendered.split("\n---\n")
|
||||
seen: set[str] = set()
|
||||
out: list[str] = []
|
||||
for doc in docs:
|
||||
name = _deployment_name(doc)
|
||||
if name in SWITCHABLE:
|
||||
seen.add(name)
|
||||
doc = _set_replicas(doc, 0 if power == "off" else 1)
|
||||
out.append(doc)
|
||||
missing = [name for name in SWITCHABLE if name not in seen]
|
||||
if missing:
|
||||
raise ValueError("manifest is missing Deployments: " + ", ".join(missing))
|
||||
return "\n---\n".join(out)
|
||||
|
||||
|
||||
def _deployment_name(doc: str) -> str | None:
|
||||
kind = None
|
||||
name = None
|
||||
for line in doc.splitlines():
|
||||
if line.startswith("kind:"):
|
||||
kind = line.split(":", 1)[1].strip()
|
||||
elif line.startswith(" name:") and name is None:
|
||||
name = line.split(":", 1)[1].strip()
|
||||
if kind == "Deployment":
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
def _set_replicas(doc: str, count: int) -> str:
|
||||
lines = doc.splitlines(keepends=True)
|
||||
found = 0
|
||||
out = []
|
||||
for line in lines:
|
||||
if line.startswith(" replicas:"):
|
||||
found += 1
|
||||
newline = "\n" if line.endswith("\n") else ""
|
||||
out.append(f" replicas: {count}{newline}")
|
||||
else:
|
||||
out.append(line)
|
||||
if found != 1:
|
||||
raise ValueError(f"expected one replicas field, found {found}")
|
||||
return "".join(out)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) != 4 or argv[1] != "render":
|
||||
print("usage: facility.py render IMAGE on|off", file=sys.stderr)
|
||||
return 2
|
||||
sys.stdout.write(render(sys.stdin.read(), argv[2], argv[3]))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
# MAS live provider test (mail-address-semantics MAS-WP-0003).
|
||||
# Founder go for A-D: 2026-10-08. Temporary: delete the namespace after the test.
|
||||
# A namespace + MX pod B LoadBalancer :25 C statute-only ingress D no outbound mail
|
||||
# D superseded 2026-10-18: operator chose replies via Scaleway TEM (Secret mas-relay,
|
||||
# synced by ExternalSecret from OpenBao platform/suppliers/scaleway/mas-tem-relay).
|
||||
# IMAGE is replaced with the pushed digest at apply time.
|
||||
# MAS facility for mas.coulomb.social (MAS-WP-0007).
|
||||
# Durable: this namespace, the evidence PVC, Services, Ingress, ExternalSecret.
|
||||
# Switchable: Deployments mas-mx and mas-statute (replicas 1 on, 0 off).
|
||||
# deploy/railiance/apply.sh reads annotation mas.coulomb.social/power and
|
||||
# renders replicas 0 when the facility is off. A missing annotation means on.
|
||||
# Replies use Scaleway TEM (Secret mas-relay, from OpenBao
|
||||
# platform/suppliers/scaleway/mas-tem-relay), capped at 300.
|
||||
# IMAGE is replaced at apply time. The running image is side-loaded.
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
|
|
@ -20,8 +22,8 @@ spec:
|
|||
---
|
||||
# TEM SMTP login from OpenBao (ops-mason MASON-WP-0014, CCR-2026-0051).
|
||||
# OpenBao role external-secrets-mas-tem-relay and its read policy are
|
||||
# cluster-scoped. Retire them at MAS-WP-0003 close; deleting the namespace
|
||||
# does not remove them.
|
||||
# cluster-scoped. They stay with the facility. Deleting the namespace would
|
||||
# not remove them, and the power switch does not delete the namespace.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata: {name: mas-tem-relay-eso, namespace: mas}
|
||||
|
|
|
|||
73
deploy/railiance/power.sh
Executable file
73
deploy/railiance/power.sh
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
#!/bin/bash
|
||||
# Turn the MAS facility on or off (MAS-WP-0007).
|
||||
# Scales only deploy/mas-mx and deploy/mas-statute. The namespace, evidence
|
||||
# PVC, Services, Ingress, and ExternalSecret stay.
|
||||
# Records the desired power on namespace mas:
|
||||
# mas.coulomb.social/power=on|off
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
ns=mas
|
||||
annotation=mas.coulomb.social/power
|
||||
deployments="mas-mx mas-statute"
|
||||
|
||||
usage() {
|
||||
echo "usage: power.sh on|off|status" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
power_now() {
|
||||
kubectl get namespace "$ns" \
|
||||
-o jsonpath='{.metadata.annotations.mas\.coulomb\.social/power}'
|
||||
}
|
||||
|
||||
wait_ready() {
|
||||
name=$1
|
||||
kubectl -n "$ns" rollout status "deploy/$name" --timeout=180s
|
||||
}
|
||||
|
||||
wait_stopped() {
|
||||
name=$1
|
||||
i=0
|
||||
while [ "$i" -lt 90 ]; do
|
||||
left=$(kubectl -n "$ns" get pods -l "app.kubernetes.io/name=$name" --no-headers 2>/dev/null | wc -l)
|
||||
left=$(echo "$left" | tr -d ' ')
|
||||
if [ "$left" -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 2
|
||||
done
|
||||
echo "pods for $name are still running" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cmd=${1:-}
|
||||
case "$cmd" in
|
||||
on|off)
|
||||
kubectl annotate namespace "$ns" "$annotation=$cmd" --overwrite
|
||||
if [ "$cmd" = on ]; then
|
||||
replicas=1
|
||||
else
|
||||
replicas=0
|
||||
fi
|
||||
for name in $deployments; do
|
||||
kubectl -n "$ns" scale "deploy/$name" --replicas="$replicas"
|
||||
done
|
||||
for name in $deployments; do
|
||||
if [ "$cmd" = on ]; then
|
||||
wait_ready "$name"
|
||||
else
|
||||
wait_stopped "$name"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
status)
|
||||
echo "power=$(power_now)"
|
||||
kubectl -n "$ns" get deploy $deployments
|
||||
kubectl -n "$ns" get endpointslice -l 'kubernetes.io/service-name in (mas-smtp,mas-statute)'
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# No owner classification file. Recorded confidential/medium for this
|
||||
# validation MX: it holds relay credentials and message evidence, and it is
|
||||
# capped rather than a production mail service.
|
||||
# No owner classification file. Recorded confidential/medium: this facility
|
||||
# holds relay credentials and message evidence. It is the operated MAS
|
||||
# facility for mas.coulomb.social and can be powered off without removing
|
||||
# that state. Replies stay capped at 300.
|
||||
kind: release-unit
|
||||
unit_id: mas
|
||||
owner: mail-address-semantics
|
||||
|
|
@ -8,7 +9,7 @@ contract_version: 1.0.0
|
|||
data_classification: confidential
|
||||
criticality: medium
|
||||
composition:
|
||||
purpose: Covers the live MAS reference MX and statute service.
|
||||
purpose: Covers the MAS facility (MX and statute), powered on or off as a pair.
|
||||
member_repos:
|
||||
- repo: mail-address-semantics
|
||||
role: rollout owner
|
||||
|
|
|
|||
74
tests/test_facility_power.py
Normal file
74
tests/test_facility_power.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""MAS facility power render (MAS-WP-0007). No cluster."""
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
_spec = importlib.util.spec_from_file_location(
|
||||
"facility", ROOT / "deploy/railiance/facility.py"
|
||||
)
|
||||
_facility = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_facility)
|
||||
render = _facility.render
|
||||
MANIFEST = (ROOT / "deploy/railiance/manifests.yaml").read_text()
|
||||
POWER = (ROOT / "deploy/railiance/power.sh").read_text()
|
||||
APPLY = (ROOT / "deploy/railiance/apply.sh").read_text()
|
||||
IMAGE = "docker.io/library/mas-live:v0.2-480e69a"
|
||||
DURABLE = (
|
||||
"kind: PersistentVolumeClaim",
|
||||
"name: mas-evidence",
|
||||
"name: mas-smtp",
|
||||
"kind: Ingress",
|
||||
"kind: ExternalSecret",
|
||||
"name: mas-relay",
|
||||
)
|
||||
|
||||
|
||||
def docs(text):
|
||||
return text.split("\n---\n")
|
||||
|
||||
|
||||
def deployment(text, name):
|
||||
for doc in docs(text):
|
||||
if doc.startswith("apiVersion: apps/v1") and f"\n name: {name}\n" in doc:
|
||||
return doc
|
||||
raise AssertionError(name)
|
||||
|
||||
|
||||
def test_off_stops_both_deployments_and_keeps_durable_objects():
|
||||
rendered = render(MANIFEST, IMAGE, "off")
|
||||
assert rendered.count("\n replicas: 0\n") == 2
|
||||
assert "replicas: 1" not in rendered
|
||||
for name in ("mas-mx", "mas-statute"):
|
||||
assert "\n replicas: 0\n" in deployment(rendered, name)
|
||||
for marker in DURABLE:
|
||||
assert marker in rendered
|
||||
assert f"image: {IMAGE}" in rendered
|
||||
assert "image: IMAGE" not in rendered
|
||||
|
||||
|
||||
def test_on_leaves_one_replica_each():
|
||||
rendered = render(MANIFEST, IMAGE, "on")
|
||||
assert rendered.count("\n replicas: 1\n") == 2
|
||||
assert "replicas: 0" not in rendered
|
||||
for marker in DURABLE:
|
||||
assert marker in rendered
|
||||
|
||||
|
||||
def test_render_rejects_unknown_power_and_a_blank_image():
|
||||
with pytest.raises(ValueError):
|
||||
render(MANIFEST, IMAGE, "standby")
|
||||
with pytest.raises(ValueError):
|
||||
render(MANIFEST, "mas live", "on")
|
||||
|
||||
|
||||
def test_operator_command_does_not_delete_durable_objects():
|
||||
assert POWER.startswith("#!/bin/bash\n")
|
||||
for script in (POWER, APPLY):
|
||||
assert "kubectl delete" not in script
|
||||
assert "delete namespace" not in script
|
||||
assert "mas-mx" in POWER and "mas-statute" in POWER
|
||||
assert "mas.coulomb.social/power" in POWER
|
||||
assert "facility.py render" in APPLY
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Operate MAS as a switchable facility"
|
||||
domain: communication
|
||||
repo: mail-address-semantics
|
||||
status: active
|
||||
status: finished
|
||||
owner: claude
|
||||
topic_slug: coulomb-social
|
||||
created: "2026-10-18"
|
||||
|
|
@ -20,7 +20,7 @@ Residual of MAS-WP-0003. Namespace `mas` is the MAS facility for
|
|||
`mas.coulomb.social`, with replies through Scaleway TEM capped at 300.
|
||||
It stays. The 2026-10-19 retirement backlog is withdrawn.
|
||||
|
||||
**Founder decision (2026-11-01): keep it, and make it switchable.** Do not
|
||||
**Founder decision (2026-11-01, `4d38ed47-8ab1-419e-839c-92921f7eeef1`): keep it, and make it switchable.** Do not
|
||||
delete the namespace, revoke the TEM key, remove DNS, or tear down the
|
||||
OpenBao role. Operate the existing facility so mail and the statute can
|
||||
be turned off and back on without discarding evidence, credentials, or
|
||||
|
|
@ -91,7 +91,7 @@ images.
|
|||
|
||||
```task
|
||||
id: MAS-WP-0007-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "18d8898e-52c9-572d-aa7c-014fb1f7751e"
|
||||
```
|
||||
|
|
@ -103,11 +103,16 @@ switchable parts and the limits above. The release-unit note stops
|
|||
calling this something other than the operated facility. Historical
|
||||
workplans and `history/` stay as they were.
|
||||
|
||||
Done 2026-11-01. `SCOPE.md`, INTENT.md criterion 5, `deploy/railiance/README.md`,
|
||||
the manifest header, and `registry/release-units/mas.yaml` describe the
|
||||
switchable facility. The 300-reply cap, side-loaded image, and snakeoil
|
||||
SMTP TLS are recorded as limits.
|
||||
|
||||
## Add the power switch
|
||||
|
||||
```task
|
||||
id: MAS-WP-0007-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "78a11c89-7cda-5ff7-bd0a-b7958b6c9fe7"
|
||||
```
|
||||
|
|
@ -124,11 +129,19 @@ ingress.
|
|||
On the cluster, record `power=on` to match the running facility. Do not
|
||||
scale it to 0 and do not roll a new image.
|
||||
|
||||
Done 2026-11-01. `deploy/railiance/power.sh` and `apply.sh`, with
|
||||
`facility.py` doing the render. Live: `power.sh on` with the railiance01
|
||||
admin kubeconfig annotated namespace `mas` as `power=on`, scaled both
|
||||
Deployments to 1, and both were already ready. Pod start times stayed
|
||||
`mas-mx` 2026-10-21T22:18:00Z and `mas-statute` 2026-10-19T11:36:30Z.
|
||||
`power.sh status` shows `power=on`, both Deployments 1/1, and endpoints
|
||||
for `mas-smtp` and `mas-statute`. No new image.
|
||||
|
||||
## Test the switch offline
|
||||
|
||||
```task
|
||||
id: MAS-WP-0007-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "3f458be1-a8c8-5b05-96e8-851294e36d60"
|
||||
```
|
||||
|
|
@ -137,3 +150,5 @@ Test the render without a cluster. Power `off` sets both Deployment
|
|||
replicas to 0 and leaves the Service, PVC, Ingress, and ExternalSecret
|
||||
in place. Power `on` leaves `replicas: 1`. The image placeholder is
|
||||
replaced. The operator command has no delete of those durable objects.
|
||||
|
||||
Done 2026-11-01. `tests/test_facility_power.py`: 4 passed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue