# The two-account, self-host deployment (plan 8.2 P9a, D-51): the books # server runs as one account, an agent as another, and the agent reaches # the books only through the server's HTTP door with a static bearer # token. `crates/ledger-cli/tests/two_user.rs` is the check; this file # makes the deployment it needs and runs it against real accounts. A # laptop's `cargo test` skips those two tests (WB_TWO_USER unset); this # job sets it, so a skip here is a failure's cousin: the job only goes # green when both tests ran. # # The token is minted as on a real server: `token create` keeps it in a # 0600 file under the root's run/ (ledger-host tokens.rs INVARIANT 3). name: two-user on: push: pull_request: workflow_dispatch: # A newer push makes an older run pointless: it is cancelled. concurrency: group: two-user-${{ github.ref }} cancel-in-progress: true jobs: two-user: runs-on: ubuntu-24.04 timeout-minutes: 45 env: WB_PORT: "17411" WB: /usr/local/bin/wanderbooks steps: - uses: actions/checkout@v4 - uses: jdx/mise-action@v2 - name: Build the server-only executable and the test run: | cargo build --release -p ledger-cli --no-default-features cargo test -p ledger-cli --test two_user --no-run - name: Two accounts, one data root only one can read run: | set -euo pipefail sudo useradd --system --create-home --home-dir /var/lib/wanderbooks \ --shell /usr/sbin/nologin wanderbooks sudo install -d -o wanderbooks -g wanderbooks -m 0700 /srv/wanderbooks sudo install -d -o wanderbooks -g wanderbooks -m 0700 /srv/desktop sudo install -d -o wanderbooks -g wanderbooks -m 0700 /srv/desktop-home sudo install -m 0755 target/release/wanderbooks "$WB" - name: The deployment, the move, and the check run: | set -euo pipefail as_books() { sudo -u wanderbooks -H env HOME=/var/lib/wanderbooks "$@"; } wait_open() { for _ in $(seq 1 240); do grep -q "The books are open" "$1" && return 0 sleep 0.5 done echo "the server did not open:" >&2 cat "$1" >&2 return 1 } stop_servers() { as_books "$WB" close --root /srv/desktop >/dev/null 2>&1 || true as_books "$WB" close --root /srv/wanderbooks >/dev/null 2>&1 || true } trap stop_servers EXIT # The machine the books come from: books of this version, made by # adopting the repository's fixture (so the move changes no event). # Its own home, so the two roots are not "somewhere else" to one # another (root::setup records one root per account home). as_books "$WB" setup --root /srv/desktop --home /srv/desktop-home as_books "$WB" serve --root /srv/desktop > /tmp/desktop.log 2>&1 & wait_open /tmp/desktop.log sudo cp -a crates/ledger-cli/tests/fixtures/p2-served /tmp/fixture # The server account adopts it, so it must own the copy: adoption # takes the source's lock (root.rs lock_source), which the runner's # ownership from the checkout refused it (the job's first runs). sudo chown -R wanderbooks:wanderbooks /tmp/fixture as_books "$WB" books adopt /tmp/fixture --root /srv/desktop --id acme --yes as_books "$WB" close --root /srv/desktop # The backup clone that is the transport (plan D-31): owned by the # server account, which adopts it (and locks it), and readable by # the agent account, because the test reads its journal to know # the head the move must keep. sudo cp -a /srv/desktop/books/acme /tmp/source sudo chown -R wanderbooks:wanderbooks /tmp/source sudo chmod -R a+rX /tmp/source # The server's machine: the same books, moved, behind `enforced`. as_books "$WB" setup --root /srv/wanderbooks as_books "$WB" serve --root /srv/wanderbooks --isolation enforced \ --http "127.0.0.1:$WB_PORT" > /tmp/tenant.log 2>&1 & wait_open /tmp/tenant.log as_books "$WB" books adopt /tmp/source --root /srv/wanderbooks --id acme --yes as_books "$WB" books create tunnel --root /srv/wanderbooks # Tokens for the agent account (the test), kept in the root's run/. as_books "$WB" token create --root /srv/wanderbooks --book acme --name ci >/dev/null TOKEN=$(as_books "$WB" token print --root /srv/wanderbooks --book acme \ --name ci) as_books "$WB" token create --root /srv/wanderbooks --book tunnel --name ci >/dev/null WRITE_TOKEN=$(as_books "$WB" token print --root /srv/wanderbooks --book tunnel \ --name ci) # The check, as the agent account, over the loopback door. WB_TWO_USER=1 \ WB_TENANT_ROOT=/srv/wanderbooks \ WB_SERVER_URL="http://127.0.0.1:$WB_PORT" \ WB_BOOK=acme \ WB_TOKEN="$TOKEN" \ WB_WRITE_BOOK=tunnel \ WB_WRITE_TOKEN="$WRITE_TOKEN" \ WB_SOURCE_DIR=/tmp/source \ cargo test -p ledger-cli --test two_user -- --nocapture --test-threads=1