pi setup

Host

Workspace cloning (ghorg)

pi-sandbox workspace builds the workspace ws/<domain>/<host>/<group>/<subgroup…>/<repo> from whole forge groups with ghorg (pinned in the image) — so every repository is a sandbox project with its configuration and memory levels (pi-config spec 009). It runs on the Mac, never inside an agent session.

Commands#

bash
pi-sandbox workspace clone   <domain> <host>/<group path> [--user] [--include-archived] [--include-forks]
pi-sandbox workspace refresh <domain> [<host>/<group path>]
pi-sandbox workspace list    [<domain>]
  • clone clones every repository of the group and its subgroups that is not there yet, and refreshes the ones that are (below). --user clones a GitHub/Codeberg user’s repositories instead of an organisation’s. Archived repositories and forks are skipped unless --include-archived / --include-forks.
  • refresh repeats clone with the options recorded for the group; without a group it runs every group recorded for the domain, one after the other.
  • list shows per domain the recorded groups (type, repository count, last run and result) and per host where its token comes from (cmd, keychain or missing). It reads no token, runs no *_CMD and starts nothing.

Layout#

Forge<group path>Result
GitLab (gitlab.com or self-hosted)group-a or group-a/sub-bws/<domain>/<host>/group-a/sub-b/…/<repo> — subgroups keep their folders
GitHub, Codeberg/Forgejo/Giteaone organisation (or user with --user)ws/<domain>/<host>/<org>/<repo>

Paths keep the forge’s spelling. After each run, every newly cloned repository’s folder is compared with the path of its remote (the host name of the clone URL is ignored: a forge may hand out another one than you addressed, and the remote is rewritten to <host> anyway); a mismatch is reported as failed (path does not match remote) and the folder is left for you to check.

Settings#

Setting (.env)ValuesDefault
WS_<ID>_<HOST_ID>_TOKEN_CMDcommand whose output is the forge token for that domain and host— (then the Keychain item ws-<domain>-<host>-token)
WS_<HOST_ID>_TYPEgitlab, github, giteainferred for gitlab.com, github.com, codeberg.org (a set value must match); required for other hosts
WS_<HOST_ID>_REMOTEssh, httpsssh
WS_<ID>_<HOST_ID>_REMOTEssh, https; this domain only, beats WS_<HOST_ID>_REMOTE—
WS_<ID>_<HOST_ID>_PUSH_KEY / _PUSH_KEY_CMD, WS_<ID>_<HOST_ID>_PUSH_TOKEN_CMD, WS_<ID>_<HOST_ID>_PUSH_USER, WS_<HOST_ID>_KNOWN_HOSTS_FILEcredential for pushing from agent sessions to that domain and host: key (remote ssh) or token (remote https)off (credentials.md)

Only the token setting names the domain; type and remote are per host. <ID> is the domain name in uppercase with - → _; <HOST_ID> the host in uppercase with . and - → _ (e.g. WS_WORK_GITLAB_EXAMPLE_COM_TOKEN_CMD, WS_GITLAB_EXAMPLE_COM_TYPE=gitlab). A Keychain item instead of the command: security add-generic-password -s pi-sandbox -a ws-<domain>-<host>-token -U -w. A token with read-only repository access is enough.

New repositories get the remote git@<host>:<path>.git (ssh) or https://<host>/<path>.git without credentials (https); you push and fetch on the Mac with your own credentials.

What happens to existing repositories#

Nothing, unless all of this holds: no uncommitted or untracked changes, no commits that are not on the forge, the default branch is checked out, and the forge’s branch is a fast-forward. Then it is fast-forwarded. Everything else is listed as skipped (<reason>) — exists, dirty, ahead of origin, on branch <b>, diverged, fetch failed, default branch unknown, remote is not on <host> — and left exactly as it was, including its remote. ghorg runs only with --no-clean (it never cleans, resets or pulls an existing repository) and never with --prune.

The workspace container and the token#

Clone and refresh run in their own container (compose.workspace.yaml): the session image and hardening (uid 1000, read-only root, no capabilities, no-new-privileges, throwaway home), and only ws/<domain> mounted — no memory, no configuration store, no agent state, no other domain. The token is read on the Mac when the command starts and passed to that container in its environment (WS_TOKEN) only. Inside, ghorg clones over HTTPS with it and resets every remote without it; refreshes pass it through a credential helper given on the git command line (nothing written). Afterwards every repository’s git config is checked for the token and cleaned; all output shows *** instead of it. Agent sessions never get a forge token (isolation self-test: “no git credentials”).

Record#

ws/<domain>/.pi-workspace/groups.json keeps one entry per <host>/<group path>: forge type, options, repository count, last run (UTC) and the counts of the last run. No secrets. refresh and list read it.

Exit codes#

2 invalid input (nothing started), 12 unknown domain, 40 container runtime not reachable, 50 the run finished with failed repositories (listed) or ghorg failed, 51 no token for that domain and host, or its *_TOKEN_CMD failed or printed nothing — usually a locked password manager (bw unlock, op signin) — (nothing started).