fix: preserve route limits and harden restore marker

This commit is contained in:
jb
2026-09-19 10:01:23 -04:00
parent 99fe7e1ca2
commit cb86497724
11 changed files with 171 additions and 30 deletions
+4
View File
@@ -29,6 +29,10 @@ The web server checks the allowlist at the relevant boundary:
- `/api/sync` and the application synchronizer check `asset_sync`.
- `/api/session/token` checks `session_token`.
- Character management and OAuth entry points check `identity`.
- The root layout skips its authenticated session lookup only when every
capability represented by the root assistant/navigation shell is disabled;
if a separate asset, identity, session-token, subscription, or assistant
capability remains enabled, it preserves the existing user session state.
- The Convex browser status subscription checks `subscriptions`; sync bridge
mutations check `asset_sync` in the backend authorization plane too. The
allowlist is copied into Convex by `bun run convex:configure`; that setting
+63 -18
View File
@@ -46,11 +46,40 @@ project="eden-restore-${USER:-operator}-$(date +%s)-$$"
restore_project="${project}-restored"
work="$(mktemp -d "${TMPDIR:-/tmp}/eden-restore.XXXXXX")"
chmod 700 "$work"
# Do not let an ambient shell, repository .env, or prior Compose project alter
# this rehearsal. The explicit env file below contains only non-secret values;
# synthetic key values are loaded separately from the mode-0600 key file.
unset COMPOSE_FILE COMPOSE_ENV_FILES COMPOSE_PROJECT_NAME COMPOSE_PROFILES DOCKER_HOST APP_MODE APP_ORIGIN ORIGIN APP_RELEASE_ID APP_CAPABILITY_ALLOWLIST CONVEX_URL PUBLIC_CONVEX_URL CONVEX_SITE_ORIGIN CONVEX_PORT CONVEX_SITE_PORT WEB_PORT CONVEX_IMAGE CONVEX_DASHBOARD_IMAGE EVE_CALLBACK_URL EVE_CLIENT_ID EVE_CLIENT_SECRET ESI_USER_AGENT TYPESAFE_API_KEY APP_AUTH_ISSUER TOKEN_ENCRYPTION_KEY CONVEX_BRIDGE_SECRET APP_AUTH_PRIVATE_KEY APP_AUTH_JWKS CONVEX_SELF_HOSTED_URL CONVEX_SELF_HOSTED_ADMIN_KEY
export CONVEX_BIND_ADDRESS=127.0.0.1 CONVEX_PORT=$((3200 + RANDOM % 500)) CONVEX_SITE_PORT=$((3700 + RANDOM % 500))
export WEB_BIND_ADDRESS=127.0.0.1 WEB_PORT=$((4200 + RANDOM % 500))
export APP_MODE=live APP_RELEASE_ID=restore-fixture APP_CAPABILITY_ALLOWLIST=asset_search,asset_valuation,asset_sync,assistant_dispatch,session_token,identity,subscriptions
export APP_ORIGIN="http://127.0.0.1:$WEB_PORT" ORIGIN="$APP_ORIGIN" APP_AUTH_ISSUER="$APP_ORIGIN"
export PUBLIC_CONVEX_URL="http://127.0.0.1:$CONVEX_PORT" CONVEX_SITE_ORIGIN="http://127.0.0.1:$CONVEX_SITE_PORT"
export EVE_CALLBACK_URL="$APP_ORIGIN/auth/eve/callback"
write_compose_env() {
cat > "$work/compose.env" <<EOF
COMPOSE_PROJECT_NAME=$project
CONVEX_BIND_ADDRESS=127.0.0.1
CONVEX_PORT=$CONVEX_PORT
CONVEX_SITE_PORT=$CONVEX_SITE_PORT
WEB_BIND_ADDRESS=127.0.0.1
WEB_PORT=$WEB_PORT
APP_MODE=$APP_MODE
APP_RELEASE_ID=$APP_RELEASE_ID
APP_CAPABILITY_ALLOWLIST=$APP_CAPABILITY_ALLOWLIST
APP_ORIGIN=$APP_ORIGIN
ORIGIN=$ORIGIN
APP_AUTH_ISSUER=$APP_AUTH_ISSUER
PUBLIC_CONVEX_URL=$PUBLIC_CONVEX_URL
CONVEX_SITE_ORIGIN=$CONVEX_SITE_ORIGIN
EVE_CALLBACK_URL=$EVE_CALLBACK_URL
EOF
}
write_compose_env
compose() { docker compose --project-directory "$repo" --file "$repo/compose.yaml" --env-file "$work/compose.env" "$@"; }
cleanup() {
docker compose -p "$project" down -v >/dev/null 2>&1 || true
docker compose -p "$restore_project" down -v >/dev/null 2>&1 || true
compose -p "$project" down -v >/dev/null 2>&1 || true
compose -p "$restore_project" down -v >/dev/null 2>&1 || true
# Export/copy the evidence bundle before this cleanup; the temporary key and
# archives are intentionally removed only after retention is recorded.
rm -rf "$work"
@@ -58,13 +87,18 @@ cleanup() {
trap cleanup EXIT
(cd "$work" && bun "$repo/scripts/generate-keys.ts")
chmod 600 "$work/.env.keys"
set -a
. "$work/.env.keys"
set +a
```
Load only those synthetic values into a temporary shell environment. Set
`APP_MODE=demo`, `APP_RELEASE_ID=restore-fixture`,
`APP_CAPABILITY_ALLOWLIST=asset_search,asset_valuation,asset_sync,assistant_dispatch,session_token,identity,subscriptions`,
and an issuer such as `http://127.0.0.1:18080`. Do not use a production
callback or provider key.
The bootstrap above loads only synthetic values into a temporary shell
environment and writes the explicit Compose env file. It allocates loopback
backend, site, web, issuer, public URL, and callback origins together; when
restore ports change, update all corresponding exported values before starting
the restored project. The Compose web service intentionally uses the
service-internal `http://backend:3210`; only host/public settings use the
allocated loopback ports. Do not use a production callback or provider key.
## Backup rehearsal
@@ -73,7 +107,7 @@ callback or provider key.
```sh
export COMPOSE_PROJECT_NAME="$project"
docker compose -p "$project" up -d backend
compose -p "$project" up -d backend
volume="${project}_convex-data"
mount="$(docker volume inspect -f '{{ .Mountpoint }}' "$volume")"
source_bytes_before="$(du -sb "$mount" | awk '{print $1}')"
@@ -87,9 +121,9 @@ callback or provider key.
key fingerprint or assume an image-specific helper command:
```sh
read -r -s CONVEX_SELF_HOSTED_ADMIN_KEY
export CONVEX_SELF_HOSTED_ADMIN_KEY
CONVEX_SELF_HOSTED_URL=http://127.0.0.1:"$CONVEX_PORT" \
read -r -s admin_key
CONVEX_SELF_HOSTED_ADMIN_KEY="$admin_key" \
CONVEX_SELF_HOSTED_URL="http://127.0.0.1:$CONVEX_PORT" \
bun "$repo/scripts/configure-convex.ts" >/dev/null
```
@@ -111,19 +145,25 @@ callback or provider key.
```sh
export RESTORE_MARKER="$(date -u +%Y%m%dT%H%M%SZ)-synthetic"
export RESTORE_MARKER_CHARACTER_ID=998000001
SMOKE_ALLOW_WRITE=1 RESTORE_MARKER="$RESTORE_MARKER" \
SMOKE_ALLOW_WRITE=1 RESTORE_MARKER_CONFIRMATION=DISPOSABLE_LOOPBACK_ONLY \
RESTORE_MARKER_PHASE=write RESTORE_MARKER="$RESTORE_MARKER" \
RESTORE_MARKER_CHARACTER_ID="$RESTORE_MARKER_CHARACTER_ID" \
CONVEX_SELF_HOSTED_URL=http://127.0.0.1:"$CONVEX_PORT" \
CONVEX_SELF_HOSTED_URL="http://127.0.0.1:$CONVEX_PORT" \
bun "$repo/scripts/smoke-restore-marker.ts" | tee "$work/marker-write.log"
```
The marker tool defaults to non-writing verification, rejects non-loopback
targets before constructing a backend client, and requires both the explicit
write phase and `DISPOSABLE_LOOPBACK_ONLY` confirmation. Never reuse the
marker command against a shared, staging, or production URL.
4. Create a consistent filesystem backup while the disposable backend is
stopped. The Convex data volume contains the backend data and instance
credential material; back up it as one unit. A host administrator may need
root access to the Docker volume mount.
```sh
docker compose -p "$project" stop backend
compose -p "$project" stop backend
tar --xattrs --acls --numeric-owner -C "$mount" \
-czf "$work/convex-data.tar.gz" .
sha256sum "$work/convex-data.tar.gz" > "$work/convex-data.tar.gz.sha256"
@@ -151,6 +191,10 @@ callback or provider key.
tar --xattrs --acls --numeric-owner -C "$restore_mount" \
-xzf "$work/convex-data.tar.gz"
export CONVEX_PORT=$((5200 + RANDOM % 500)) CONVEX_SITE_PORT=$((5700 + RANDOM % 500)) WEB_PORT=$((6200 + RANDOM % 500))
export APP_ORIGIN="http://127.0.0.1:$WEB_PORT" ORIGIN="$APP_ORIGIN" APP_AUTH_ISSUER="$APP_ORIGIN"
export PUBLIC_CONVEX_URL="http://127.0.0.1:$CONVEX_PORT" CONVEX_SITE_ORIGIN="http://127.0.0.1:$CONVEX_SITE_PORT"
export EVE_CALLBACK_URL="$APP_ORIGIN/auth/eve/callback"
write_compose_env
```
2. Start the restored backend under the second project and configure the web
@@ -160,8 +204,9 @@ callback or provider key.
instance-credential continuity is unproven and traffic must remain closed:
```sh
docker compose -p "$restore_project" up -d backend
CONVEX_SELF_HOSTED_URL=http://127.0.0.1:"$CONVEX_PORT" \
compose -p "$restore_project" up -d backend
CONVEX_SELF_HOSTED_ADMIN_KEY="$admin_key" \
CONVEX_SELF_HOSTED_URL="http://127.0.0.1:$CONVEX_PORT" \
bun "$repo/scripts/configure-convex.ts" >/dev/null
```
@@ -169,9 +214,9 @@ callback or provider key.
the synthetic Convex smoke with fresh IDs:
```sh
SMOKE_ALLOW_WRITE=1 RESTORE_MARKER="$RESTORE_MARKER" \
RESTORE_MARKER="$RESTORE_MARKER" \
RESTORE_MARKER_CHARACTER_ID="$RESTORE_MARKER_CHARACTER_ID" \
CONVEX_SELF_HOSTED_URL=http://127.0.0.1:"$CONVEX_PORT" \
CONVEX_SELF_HOSTED_URL="http://127.0.0.1:$CONVEX_PORT" \
RESTORE_MARKER_PHASE=verify bun "$repo/scripts/smoke-restore-marker.ts"
```