Compare commits

...

2 commits

5 changed files with 194 additions and 8 deletions

View file

@ -0,0 +1,126 @@
#!/usr/bin/env bash
# The git host (main, 7 October 2026 20:29 BST; GitHub suspended): Forgejo 9 under docker on build-1, data under /srv/git (config
# app.ini written once with generated secrets, kept on the box only), ssh on 2222 public, http 3000 on loopback behind Caddy at
# git.igneum.network (ACME), deSEC A record by hand (dns.sh pattern). Then, by hand through the API as the owner user igneum-labs
# (credentials in ~/.config/igneum/forgejo-admin on the Mac): org igneum-network, private repos igneum and spec, the Mac key on the
# owner; registration off, anonymous read off, push-to-create off. Nightly dump to build-2 by /usr/local/bin/forgejo-backup.sh
# (forgejo-backup.timer 03:30 UTC). Runs as root on the box: scp it over and run it. Idempotent.
set -euo pipefail
mkdir -p /srv/git/data /srv/git/config
chown -R 1000:1000 /srv/git
if [ ! -s /srv/git/config/app.ini ]; then
SECRET=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
JWT=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
LFSJWT=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
cat > /srv/git/config/app.ini <<INI
APP_NAME = Igneum
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /data/gitea
[server]
PROTOCOL = http
DOMAIN = git.igneum.network
ROOT_URL = https://git.igneum.network/
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
SSH_DOMAIN = git.igneum.network
SSH_PORT = 2222
SSH_LISTEN_PORT = 22
START_SSH_SERVER = false
DISABLE_SSH = false
LFS_START_SERVER = true
LFS_JWT_SECRET = $LFSJWT
OFFLINE_MODE = true
LANDING_PAGE = login
[database]
DB_TYPE = sqlite3
PATH = /data/gitea/forgejo.db
[repository]
ROOT = /data/git/repositories
DEFAULT_PRIVATE = private
FORCE_PRIVATE = true
ENABLE_PUSH_CREATE_USER = false
ENABLE_PUSH_CREATE_ORG = false
DEFAULT_BRANCH = master
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
ENABLE_NOTIFY_MAIL = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
[security]
INSTALL_LOCK = true
SECRET_KEY = $SECRET
INTERNAL_TOKEN = $JWT
PASSWORD_HASH_ALGO = argon2
MIN_PASSWORD_LENGTH = 16
[oauth2]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false
[mailer]
ENABLED = false
[session]
PROVIDER = file
COOKIE_SECURE = true
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /data/gitea/log
[other]
SHOW_FOOTER_VERSION = false
[actions]
ENABLED = false
[packages]
ENABLED = false
[federation]
ENABLED = false
INI
chown 1000:1000 /srv/git/config/app.ini; chmod 600 /srv/git/config/app.ini
fi
if ! docker ps -a --format '{{.Names}}' | grep -qx forgejo; then
docker run -d --name forgejo --restart unless-stopped \
-e USER_UID=1000 -e USER_GID=1000 -e FORGEJO_CUSTOM=/data/gitea \
-v /srv/git/data:/data -v /srv/git/config/app.ini:/data/gitea/conf/app.ini \
-v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro \
-p 127.0.0.1:3000:3000 -p 2222:22 \
--cpus 4 --memory 8g \
codeberg.org/forgejo/forgejo:9 >/dev/null
fi
ufw allow 2222/tcp >/dev/null
if ! grep -q "^git.igneum.network" /etc/caddy/Caddyfile; then
cat >> /etc/caddy/Caddyfile <<'CADDY'
# the git host (Forgejo in docker, infra/build-server/forgejo.sh): everything proxied, nothing cached
git.igneum.network {
tls {
issuer acme
}
reverse_proxy 127.0.0.1:3000
request_body {
max_size 2GB
}
}
CADDY
caddy fmt --overwrite /etc/caddy/Caddyfile >/dev/null 2>&1 || true
systemctl reload caddy
fi
for i in $(seq 1 30); do curl -fsS -o /dev/null http://127.0.0.1:3000/ 2>/dev/null && break; sleep 2; done
echo "forgejo container: $(docker ps --format '{{.Names}} {{.Status}}' | grep forgejo)"
echo "http 3000: $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/)"
echo "ssh 2222: $(timeout 5 bash -c 'exec 3<>/dev/tcp/127.0.0.1/2222; head -c 40 <&3' 2>/dev/null | tr -d '\r\n' | cut -c1-40)"

View file

@ -480,18 +480,27 @@ BR_RUN_LOG="$RUN_LOG_DIR/$BR_HOST-$BR_T0-$BR_PID.log"; export BR_RUN_LOG
# (nice 0, a gate or a release build) is outside the pool and keeps the full set.
ncpu=$(nproc); cores_str="0-$((ncpu - 1))"
pool_fds=(); pool_cores=()
# the pool's priority classes (lease.sh, main 7 Oct 2026 20:37 BST: release > v5 > measure > adv): a bounded build or suite is class
# release (rank 0) unless its label names a "v5 gate" or "v5 kit" (class v5, rank 1); the wait file carries "class <name>/<rank>:" so
# `lease pool` sweeps (class adv) yield to it, and it yields while a higher class waits (nothing outranks release)
pool_class() { case "$BR_LABEL" in *"v5 gate"*|*"v5 kit"*) echo "v5/1" ;; *) echo "release/0" ;; esac; }
pool_take() { # <want> <min>: lease up to <want> free pool cores, at least <min>, waiting up to 2 h; sets pool_cores/pool_fds
local want="$1" min="$2" t0 c fd taken
local want="$1" min="$2" t0 c fd taken cls rank higher
cls=$(pool_class); rank=${cls#*/}
t0=$(date +%s)
while :; do
taken=0; pool_fds=(); pool_cores=()
higher=$(cat "$SLOTS_DIR"/wait-* 2>/dev/null | grep -v "^pid $BR_PID " | sed -n 's/.* class [a-z0-9]*\/\([0-9]\):.*/\1/p' | awk -v r="$rank" '$1 < r' | head -1)
if [ -z "$higher" ]; then
for ((c = ${BR_POOL_RESERVE:-8}; c < ncpu && taken < want; c++)); do
exec {fd}>>"$SLOTS_DIR/core-$c"
if flock -n "$fd"; then pool_fds+=("$fd"); pool_cores+=("$c"); taken=$((taken + 1)); else exec {fd}>&-; fi
done
if [ "$taken" -ge "$min" ]; then return 0; fi
for fd in "${pool_fds[@]}"; do exec {fd}>&-; done; pool_fds=(); pool_cores=()
[ -f "$waitfile" ] || { echo "build-remote: the bounded pool has $taken free core(s) (want $want, at least $min): waiting for the pool" >&2; holder_line "$(( $(date +%s) - BR_T0 ))" > "$waitfile"; }
fi
[ -f "$waitfile" ] || { echo "build-remote: the bounded pool has $taken free core(s) (want $want, at least $min, class ${cls%/*}): waiting for the pool" >&2; }
printf '%s class %s: pool\n' "$(holder_line "$(( $(date +%s) - BR_T0 ))")" "$cls" > "$waitfile"
[ $(( $(date +%s) - t0 )) -lt 7200 ] || give_up "the bounded pool"
sleep 10
done

View file

@ -5,3 +5,7 @@
| no text overlaps (`overlap-check.mjs`) | A served page, or a miner or wallet screen (behind `IGNEUM_OVERLAP_APPS=1`), where a visible run of text is covered by another element (a pill over a caption, a label over a value, a card over its neighbour, text under the header at rest), clipped by an overflow-hidden ancestor, or past the viewport; a page that scrolls sideways. Five widths, light and dark, the home hero at rest and at each step. A fixture with one deliberate overlap of each kind must be flagged first (`--self-test`). Needs a headless Chromium: CI installs Playwright; the Mac ships the pages to build-2 (`infra/build-server/overlap-browser.sh`). | 7 October 2026: the hero's step pill sat on the caption's second line ("Two thirds of the weight sign. The checkpoint locks.") at every desktop width, found by the project lead on the live site |
| kill by exact command or pid file (owed as a check) | 6 October 2026, 21:09Z: a Mac-side `pkill -f <log file name>` matched nothing (the log name was a redirect, not part of the command line), the roll-everything script lived on and wiped a box it had been told to hold. Rule: a job is stopped by its pid file (`tools/fleet/fleet-bg.sh start|stop <name>`) or by a pattern anchored on its exact command line (`^python3 -u /root/fleet/in/box-prover.py`), never by a word that may or may not appear in it. The check that flags a `pkill -f`/`pgrep -f` whose literal is a path or a name that never starts a command line is owed to the CI lane |
## No inline deletion in a shell string (7 October 2026)
The desktop app asks the founder to approve any shell command that carries `rm` inside an inline `bash -c '...'` or `sh -c '...'` string ("runs rm and could not be checked"). Rule for every lane: never an inline `rm`, `rm -rf`, `find ... -delete` or a redirect-truncate inside a `bash -c` / `sh -c` string. Put the script in a file under `tools/` (or the lane's scratch directory) and run it by path; on the boxes, do deletions through the lease or job tooling, which the checker reads as a plain command. `tools/ci/inline-rm-check.sh` greps every tracked script for the shape (self-test first, known-failed shapes named) and runs in the gate.

46
tools/ci/inline-rm-check.sh Executable file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env bash
# No inline deletion inside a `bash -c` or `sh -c` string (main, 7 October 2026 21:33 BST: the desktop app asked the founder to
# approve lanes' shell commands that carried `rm` inside an inline bash -c '...' string, "runs rm and could not be checked").
# Rule: never an inline `rm`, `rm -rf`, `find ... -delete` or a redirect-truncate (`: > file`, `> file` on its own) inside a
# `bash -c` / `sh -c` string in a tracked script; put the script in a file under tools/ (or the lane's scratch directory) and run it
# by path; on the boxes, deletions go through the lease or job tooling, which the checker reads as a plain command.
# This check greps every tracked shell, PowerShell and JS/MJS script for a bash -c / sh -c string that carries one of those. Known
# failures named with file and line. --self-test first: four banned shapes fail, four allowed shapes pass.
set -uo pipefail
cd "$(git rev-parse --show-toplevel)"
# a line that opens an inline shell string (bash -c, sh -c, "bash", "-c" in a PowerShell or JS argument list) and, on the same
# line, a deletion word inside it
BANNED='((bash|sh|pwsh|powershell)(\.exe)? +-l?c +["'"'"'][^"'"'"']*|"-c", *["'"'"'][^"'"'"']*)(\brm +|\brm$|find [^"'"'"']*-delete|(^|[ ;&|]): *> *[^ ]|[ ;&|]> *([A-Za-z._$]|/[a-ce-z]|/d[a-df-z])[^ ]* *([;&|]|$))'
scan() { # <file...>: prints "file:line: text" for each hit
grep -n -H -E "$BANNED" "$@" 2>/dev/null | grep -v -E '^[^:]*:[0-9]+:\s*#' || true
}
if [ "${1:-}" = --self-test ]; then
t=$(mktemp -d); trap 'rm -rf "$t"' EXIT; fail=0
printf '%s\n' 'ssh box "bash -c '"'"'cd /tmp && rm -rf /tmp/x'"'"'"' > "$t/b1.sh"
printf '%s\n' 'sh -c "find /srv/x -name y -delete"' > "$t/b2.sh"
printf '%s\n' 'bash -c '"'"': > /srv/builds/_locks/quiet'"'"'' > "$t/b3.sh"
printf '%s\n' 'Start-Process bash -ArgumentList "-c", "rm /tmp/old.log; echo ok"' > "$t/b4.ps1"
printf '%s\n' 'bash tools/ci/clean-scratch.sh /tmp/x' > "$t/a1.sh"
printf '%s\n' 'rm -rf "$t" # a plain command, the checker reads it' > "$t/a2.sh"
printf '%s\n' 'ssh box "bash -c '"'"'ls /srv/x && echo done'"'"'"' > "$t/a3.sh"
printf '%s\n' '# bash -c "rm -rf x" is banned (a comment names the rule)' > "$t/a4.sh"
for f in b1.sh b2.sh b3.sh b4.ps1; do [ -n "$(scan "$t/$f")" ] || { echo "inline-rm self-test: FAIL: banned shape $f passed"; fail=1; }; done
for f in a1.sh a2.sh a3.sh a4.sh; do [ -z "$(scan "$t/$f")" ] || { echo "inline-rm self-test: FAIL: allowed shape $f was flagged: $(scan "$t/$f")"; fail=1; }; done
[ "$fail" = 0 ] && echo "inline-rm self-test: 4 banned shapes fail (rm, find -delete, truncate, PowerShell -c list), 4 allowed shapes pass (script by path, plain rm, no deletion, a comment)"
[ "$fail" = 0 ] || exit 1
fi
# allowed for now, named: the proving lane's WSL socket clean-up (tools/proving-v1, three lines of `bash -c 'pkill ...; rm -f /tmp/sp1-cuda-*.sock'`
# run inside WSL on a PC by a job, not from a Mac shell); the lane moves it into a file under tools/proving-v1 and the lines leave this list
ALLOW='^tools/proving-v1/(pc2-agg-cost(-restore)?|pc2-segments)\.ps1:'
hits=""
while IFS= read -r f; do
[ -n "$f" ] || continue
h=$(scan "$f" | grep -v -E "$ALLOW" || true)
[ -n "$h" ] && hits="${hits}${h}"$'\n'
done < <(git ls-files -- '*.sh' '*.bash' '*.ps1' '*.mjs' '*.js' '*.yml' '*.yaml' | grep -v -E '^tools/ci/inline-rm-check\.sh$')
hits=$(printf '%s' "$hits" | sed '/^$/d')
if [ -n "$hits" ]; then
echo "inline-rm: a deletion inside an inline bash -c / sh -c string (put the script in a file and run it by path; on a box use the lease or job tooling):"
echo "$hits" | cut -c1-200 | sed 's/^/ /'; exit 1
fi
echo "inline-rm: no tracked script carries rm, find -delete or a truncate inside an inline bash -c / sh -c string"

View file

@ -104,6 +104,7 @@ tree_checks() {
run "long-running tools keep their body in one parsed block (the edited-while-running class)" bash -c 'bash tools/ci/whole-body-check.sh --self-test && bash tools/ci/whole-body-check.sh'
run "build-remote without a priority flag bounds suites and benches (nice 10, 32 cores); a gate runs unbounded" bash tools/ci/build-kind-default-check.sh
run "the class router is a preference with spill-over (a held or overloaded box hands the job to the other one)" bash tools/ci/route-spill-check.sh
run "no deletion inside an inline bash -c / sh -c string in a tracked script (self-test first; the app cannot check it)" bash tools/ci/inline-rm-check.sh --self-test
run "per-core leases, the quiet class and the reaper pass on the box (lease.sh and remote-run.sh self-tests over ssh)" bash tools/ci/box-locks-check.sh $( [ "$MODE" = ci ] && echo --ci )
run "the simulators job runs on master and release-* pushes and pull requests into them only" bash tools/ci/sims-branch-check.sh
run "publish-jobs.sh never removes a running or installs-app job without --force (the PC 2 abort class)" bash tools/ci/publish-jobs-check.sh