feat: password Realm, single-chair login, shared parliament context (0.9.12)

Derive Realm keys from a user password (Argon2id, no private key on disk).
Login claims the sole chair with handover; peers share parliament context;
--new-project clears it. CLI peer/discover helpers and Docker alpine recipe
included; headless empty-answer retries raised for unattended runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-10-28 23:06:51 +08:00
co-authored by Cursor
parent 08ca613a0e
commit de9ee28773
18 changed files with 1101 additions and 8 deletions
+14
View File
@@ -0,0 +1,14 @@
# Build inside Alpine so the binary does not depend on the host Nix store.
FROM alpine:latest AS build
RUN apk add --no-cache build-base curl-dev ncurses-dev pkgconf python3
WORKDIR /src
COPY . .
# Skip WebUI asset embedding quirks in minimal images if needed; keep default on.
RUN make -j"$(nproc)" cst-agent
FROM alpine:latest
RUN apk add --no-cache libcurl ncurses-libs ca-certificates
COPY --from=build /src/cst-agent /usr/local/bin/cst-agent
ENV XDG_CONFIG_HOME=/realm-config
WORKDIR /work
ENTRYPOINT ["cst-agent"]
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Real Docker smoke: shared Realm volume, chair listens, member dials.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
IMG="${PARLIAMENT_IMAGE:-cst-parliament:local}"
NET="${PARLIAMENT_NET:-cst-parliament-net}"
VOL="${PARLIAMENT_VOL:-cst-parliament-realm}"
LISTEN_PORT=9454
cd "$ROOT"
echo "== docker build (alpine) =="
docker build -f scripts/Dockerfile.parliament -t "$IMG" .
docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET"
docker volume inspect "$VOL" >/dev/null 2>&1 || docker volume create "$VOL"
cleanup() {
docker rm -f cst-parl-a cst-parl-b >/dev/null 2>&1 || true
}
trap cleanup EXIT
cleanup
docker volume rm "$VOL" >/dev/null 2>&1 || true
docker volume create "$VOL" >/dev/null
echo "== realm + enroll on shared volume =="
docker run --rm -v "$VOL:/realm-config" -e XDG_CONFIG_HOME=/realm-config "$IMG" --realm-new
docker run --rm -v "$VOL:/realm-config" -e XDG_CONFIG_HOME=/realm-config "$IMG" \
--realm-enroll chair-a --realm-label chair
docker run --rm -v "$VOL:/realm-config" -e XDG_CONFIG_HOME=/realm-config "$IMG" \
--realm-enroll member-b --realm-label member
echo "== chair listens =="
docker run -d --name cst-parl-a --network "$NET" \
-v "$VOL:/realm-config" -e XDG_CONFIG_HOME=/realm-config \
"$IMG" --peer-listen="$LISTEN_PORT" --instance chair-a
for i in $(seq 1 50); do
if docker logs cst-parl-a 2>&1 | grep -q "peer_listen port="; then
break
fi
sleep 0.2
done
docker logs cst-parl-a 2>&1 | tail -8
echo "== member dials =="
docker run --rm --name cst-parl-b --network "$NET" \
-v "$VOL:/realm-config" -e XDG_CONFIG_HOME=/realm-config \
"$IMG" --peer-dial="cst-parl-a:${LISTEN_PORT}" --instance member-b
echo "== chair result =="
for i in $(seq 1 50); do
if docker logs cst-parl-a 2>&1 | grep -q "peer_ok"; then
break
fi
sleep 0.2
done
docker logs cst-parl-a 2>&1 | grep peer_ok
echo "docker_parliament_test: OK"