Status: preparation only. No host was confirmed, no artifact was installed, and no service was started or activated. Host size/provider and account state remain UNKNOWN. This page replaces the draft "Stage C" handoff in the PR body; that draft command is superseded and must not be run.
This is a bounded, receipt-gated handoff for the single persistent Linux x86_64 environment. It intentionally contains no merge, release, install, activation, purchase, provisioning, network/auth change, or live model test.
Every identity below is exact and independently recorded. Nothing is
discovered from a moving branch, latest, or a release channel.
| Component | Identity | Notes |
|---|---|---|
| T3 base | 419f7574010c066a56974fc9e3ac0709a08efb33 |
origin/main at preparation |
| T3 source repair | branch fix/opencode-missing-session-continuation-20260928 |
not in any published binary |
| Installer source | nullStack65/t3code scripts/install.sh @ 419f7574010c066a56974fc9e3ac0709a08efb33 |
reviewed bytes, see §3 |
| Installer SHA-256 | e2462ba995aaa2773872f1fe9f2ccee53094d4ba6a4207dbc5115a65710b8a0a (9838 bytes) |
recomputed from the pinned commit |
| Published baseline release | tag v0.0.43, published 2026-09-28 |
binary source 929b63795e7696855ada61de5fd359dc2f51da78, distinct from main |
| Baseline Linux archive | t3-0.0.43-linux-x64.tar.gz, asset ID 595218686, 64106782 bytes, SHA-256 a8d8a519dc572451f19167246fdba0d8eb92cf7d53ec498097b0b3e636c81772 |
does not contain this fix |
| Pilot candidate release | UNISSUED | a repair-containing release does not exist yet |
| Canonical config | nullStack65/closura-agent-config master b60a29788c62a242b5ca3968075879956b0294ba |
harnesses/setup/adapters.yaml#opencode, company/agent-environment/contract/candidate/environment-release.candidate.yaml |
| OpenCode version | 1.17.9 |
the only version with a probed MCP rendering; see §2 |
| OpenCode linux-x64 artifact SHA-256 | UNRECORDED | required receipt; see §2 |
| Provider route | native OpenCode CLI, own auth | loopback gateway contract is source-only and not a prerequisite |
| Recovery direction | host-level Tailscale primary (ENV-1 closura-agent-config#237) |
Cloudflare conditional/unselected; keep T3 private |
Baseline vs. repair. v0.0.43 predates the missing-session repair. A future
artifact containing the repair must be built from a commit at or after this PR.
Do not relabel v0.0.43, current main, or this patch as containing the fix,
and do not move its tag or assets.
- T3 runtime accepts
opencode >= 1.14.19(MINIMUM_OPENCODE_VERSIONinapps/server/src/provider/opencodeRuntime.ts). - The Closura setup adapter only declares a probed MCP rendering for
>=1.17.9 <2.0.0, probed at1.17.9; earlier 1.x minors are not claimed and fail closed, and 2.x fails closed (harnesses/setup/adapters.yaml#opencode@b60a297). - Therefore the managed pilot input is the exact version
1.17.9. The runtime floor1.14.19is a T3 acceptance minimum, not a qualified managed input; do not use1.14.19–1.17.8. - The provider profile is Windows/macOS only; there is no Linux provider
binding. The environment candidate records
linux-x64: { binary: null, version: null }foropencodeand no artifact digest. The native OpenCode route does not depend on that loopback contract.
Precise missing receipts (owners unchanged).
| Missing receipt | Owner |
|---|---|
Exact OpenCode 1.17.9 linux-x64 artifact SHA-256 and size |
setup/adapter owner (harnesses/setup/adapters.yaml#opencode) |
environment-release.candidate.yaml opencode.linux-x64 binary/version/digest |
agent-environment candidate owner (ENV-1) |
| Repair-containing T3 release (version, archive size/hash, binary source) | T3 release owner |
| Target host identity and account/access confirmation | ENV-1 |
No hash, route, or Linux qualification is invented here.
This reuses the reviewed installer mechanism; it is not a new downloader. The
guard exits before any download, extraction, or symlink while a required
receipt is unset/UNISSUED, then verifies the installer and archive digests it
fetched. It then exposes only those verified bytes through a private
loopback staging mirror and points the installer's existing
T3CODE_RELEASE_BASE_URL at it. There is no second upstream download, an
ambient T3CODE_RELEASE_BASE_URL cannot redirect the fetch, and a stale
.install-complete marker cannot skip consumption of the verified archive. Run
as an ordinary user, never root. This is a dormant install: it does not start a
service. Requires curl, tar, sha256sum (or shasum), and python3 for
the private staging mirror.
The installer and archive precheck sources are overridable only through
T3_INSTALLER_SOURCE_URL / T3_ARCHIVE_SOURCE_URL; those bytes remain bound by
T3_INSTALLER_SHA256 / T3_ARCHIVE_SHA256+T3_ARCHIVE_SIZE, which the retained
offline regression test (scripts/pilot-handoff.test.ts) uses with small local
fixtures. The installer itself always runs from the verified local copy.
What this binds. The guard binds the bytes of the installer and of the
release archive (size + SHA-256) and refuses a stale marker, so the archive
the installer extracts is exactly the archive this guard verified. It does
not establish build, platform, or provenance for the binary inside that
archive: T3_BINARY_SOURCE and OPENCODE_LINUX_SHA256 are format-checked
recorded receipts, not verified bindings. A nonempty T3_BINARY_SOURCE is a
recorded commit, not a proven build origin, and the OpenCode 1.17.9 linux-x64
artifact remains UNRECORDED (§2) until its owner supplies the digest. Do not
read this guard as a Linux qualification.
#!/bin/sh
# Dormant, receipt-gated install guard. Fails closed before any mutation.
set -eu
# Reviewed installer bytes, pinned by full commit + independently recorded digest.
T3_INSTALLER_REPO="${T3_INSTALLER_REPO:-nullStack65/t3code}"
T3_INSTALLER_COMMIT="${T3_INSTALLER_COMMIT:-419f7574010c066a56974fc9e3ac0709a08efb33}"
T3_INSTALLER_SHA256="${T3_INSTALLER_SHA256:-e2462ba995aaa2773872f1fe9f2ccee53094d4ba6a4207dbc5115a65710b8a0a}"
T3_INSTALLER_SOURCE_URL="${T3_INSTALLER_SOURCE_URL:-https://raw.githubusercontent.com/${T3_INSTALLER_REPO}/${T3_INSTALLER_COMMIT}/scripts/install.sh}"
# Pilot-candidate receipts. UNISSUED until a release owner records them.
T3_VERSION="${T3_VERSION:-UNISSUED}"
T3_ARCHIVE_SHA256="${T3_ARCHIVE_SHA256:-UNISSUED}"
T3_ARCHIVE_SIZE="${T3_ARCHIVE_SIZE:-UNISSUED}"
T3_BINARY_SOURCE="${T3_BINARY_SOURCE:-UNISSUED}"
OPENCODE_VERSION="${OPENCODE_VERSION:-UNISSUED}"
OPENCODE_LINUX_SHA256="${OPENCODE_LINUX_SHA256:-UNISSUED}"
T3_ARCHIVE_SOURCE_URL="${T3_ARCHIVE_SOURCE_URL:-https://github.com/${T3_INSTALLER_REPO}/releases/download/v${T3_VERSION}/t3-${T3_VERSION}-linux-x64.tar.gz}"
die() { printf 'refusing to install: %s\n' "$1" >&2; exit "$2"; }
is_hex() { printf '%s' "$1" | grep -Eq "^[0-9a-f]{$2}$"; }
checksum() {
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
else shasum -a 256 "$1" | cut -d' ' -f1; fi
}
for name in T3_VERSION T3_ARCHIVE_SHA256 T3_ARCHIVE_SIZE T3_BINARY_SOURCE \
OPENCODE_VERSION OPENCODE_LINUX_SHA256; do
eval "value=\${$name}"
case "$value" in
""|UNISSUED)
die "receipt $name is unset/UNISSUED" 78 ;;
esac
done
is_hex "$T3_INSTALLER_SHA256" 64 || die "T3_INSTALLER_SHA256 is not a 64-hex digest" 65
is_hex "$T3_ARCHIVE_SHA256" 64 || die "T3_ARCHIVE_SHA256 is not a 64-hex digest" 65
is_hex "$T3_BINARY_SOURCE" 40 || die "T3_BINARY_SOURCE is not a 40-hex commit" 65
is_hex "$OPENCODE_LINUX_SHA256" 64 || die "OPENCODE_LINUX_SHA256 is not a 64-hex digest" 65
printf '%s' "$T3_ARCHIVE_SIZE" | grep -Eq '^[0-9]+$' || die "T3_ARCHIVE_SIZE is not an integer" 65
work="$(mktemp -d)"; server_pid=
cleanup() { [ -z "$server_pid" ] || kill "$server_pid" 2>/dev/null || true; rm -rf "$work"; }
trap cleanup EXIT INT TERM
# 1. Fetch the installer at the pinned COMMIT (never `main`) and verify its
# exact bytes before it can run.
curl -fsSL "$T3_INSTALLER_SOURCE_URL" -o "$work/install.sh"
[ "$(checksum "$work/install.sh")" = "$T3_INSTALLER_SHA256" ] || die "installer digest mismatch" 65
# 2. Independently download and verify the release archive BEFORE the installer
# consumes it. The release's own SHA256SUMS sits beside the mutable archive
# and is not trusted alone; T3_ARCHIVE_SHA256/SIZE are the authoritative receipt.
curl -fsSL "$T3_ARCHIVE_SOURCE_URL" -o "$work/archive.tar.gz"
[ "$(wc -c < "$work/archive.tar.gz" | tr -d ' ')" = "$T3_ARCHIVE_SIZE" ] || die "archive size mismatch" 65
[ "$(checksum "$work/archive.tar.gz")" = "$T3_ARCHIVE_SHA256" ] || die "archive digest mismatch" 65
# 3. Expose ONLY the verified bytes through a private loopback staging mirror,
# so the installer's own download extracts exactly the archive just verified.
archive_name="t3-${T3_VERSION}-linux-x64.tar.gz"
mirror="$work/mirror"; mkdir -p "$mirror/v${T3_VERSION}"
cp "$work/archive.tar.gz" "$mirror/v${T3_VERSION}/${archive_name}"
printf '%s %s\n' "$T3_ARCHIVE_SHA256" "$archive_name" > "$mirror/v${T3_VERSION}/SHA256SUMS"
port="$(python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",0));print(s.getsockname()[1]);s.close()')"
( cd "$mirror" && exec python3 -m http.server "$port" --bind 127.0.0.1 >/dev/null 2>&1 ) &
server_pid=$!
i=0
while ! curl -fsS "http://127.0.0.1:${port}/v${T3_VERSION}/SHA256SUMS" >/dev/null 2>&1; do
i=$((i + 1)); [ "$i" -lt 100 ] || die "staging mirror did not start" 69
sleep 0.1
done
# 4. Refuse a stale marker: the verified bytes must actually be consumed, never
# skipped because something already looks installed.
t3_home="${T3CODE_HOME:-$HOME/.t3}"
if [ -e "${t3_home}/runtime/versions/${T3_VERSION}/.install-complete" ]; then
die "isolated target already has an install marker for ${T3_VERSION}; use an empty target" 65
fi
# 5. Run the reviewed, digest-verified installer. These assignments deliberately
# override any ambient T3CODE_RELEASE_BASE_URL.
T3CODE_VERSION="$T3_VERSION" \
T3CODE_RELEASE_REPOSITORY="$T3_INSTALLER_REPO" \
T3CODE_RELEASE_BASE_URL="http://127.0.0.1:${port}" \
T3CODE_HOME="$t3_home" \
T3CODE_INSTALL_BIN_DIR="${T3CODE_INSTALL_BIN_DIR:-$HOME/.local/bin}" \
sh "$work/install.sh"The v0.0.43 baseline can be installed the same way for a non-candidate
smoke check by setting T3_VERSION=0.0.43,
T3_ARCHIVE_SIZE=64106782,
T3_ARCHIVE_SHA256=a8d8a519dc572451f19167246fdba0d8eb92cf7d53ec498097b0b3e636c81772,
and T3_BINARY_SOURCE=929b63795e7696855ada61de5fd359dc2f51da78. It does not
contain the repair; do not present it as the pilot candidate.
- A — target verification (read-only). Confirm
uname -srmisLinux … x86_64, a livesystemctl --user status, and record disk/RAM.which opencode && opencode --versionmust be the exact pinned1.17.9. State UNKNOWN rather than inventing a host id. - B — access preparation. Join the host to the tailnet under the existing ENV-1 Tailscale direction. Never request credentials in chat or GitHub and never invent an IP/host id. Keep T3 loopback/private; production-control credentials stay outside ordinary coding authority. An ordinary user owns the install.
- C — dormant artifact installation. Run the §3 guard with the receipts set.
No service is started;
install.shonly downloads, verifies, extracts, and symlinks. - D — activation (explicitly out of scope).
t3 service installstarts the service and may enable lingering. For an attended run use foregroundt3 serve. Recovery/stop:t3 service status,t3 service restart,t3 service uninstall, andsudo loginctl enable-linger "$(id -un)"only if status reports linger-disabled.
One sample every 60 s, hard stop after 604800 s, cumulative output
capped at 50 MiB. Existing tools only; no daemon or dashboard. Any metric
that cannot be read is recorded unavailable, never guessed. The cap is checked
on the exact UTF-8 byte length before the line is written, and the terminal
status text counts toward it, so the file can never exceed the cap. Each probe
is a bounded subprocess (killed at MEASURE_PROBE_TIMEOUT), and the sleep never
runs past the hard stop. df measures filesystem capacity/free space, not
the growth of any individual directory; no recursive directory scans are
performed. CPU counters are recorded as raw, named /proc/stat fields including
steal; memory uses MemAvailable (not MemFree) and records swap where
available; memory pressure (PSI) is recorded where available.
#!/bin/sh
# Bounded one-week sampler. Reads only counters; never prompts, args, or content.
if ! command -v timeout >/dev/null 2>&1 || ! timeout --version 2>/dev/null | grep -q 'GNU coreutils'; then
echo "GNU coreutils timeout is required for bounded probes" >&2
exit 1
fi
interval="${MEASURE_INTERVAL:-60}"; max_seconds="${MEASURE_MAX_SECONDS:-604800}"
cap_bytes="${MEASURE_CAP_BYTES:-52428800}"; probe_timeout="${MEASURE_PROBE_TIMEOUT:-5}"
proc_root="${MEASURE_PROC_ROOT:-/proc}"
out="${MEASURE_OUT:-$HOME/t3-opencode-pilot-$(date +%Y%m%dT%H%M%SZ)}"
now="${MEASURE_NOW:-date +%s}"; nap="${MEASURE_SLEEP:-sleep}"
# timeout runs each probe in its own process group and kills the whole group
# when it exceeds probe_timeout, so grandchildren cannot outlive the sampler.
bounded() {
secs="$1"; shift
timeout --signal=TERM --kill-after=1s "${secs}s" "$@"
}
mkdir -p "$out"; log="$out/monitor.log"; total=0
end=$(( $($now) + max_seconds ))
while :; do
[ "$($now)" -lt "$end" ] || break
if [ -n "${MEASURE_PROBE:-}" ]; then
line="$(bounded "$probe_timeout" sh -c "$MEASURE_PROBE" 2>/dev/null || true)"
else
cpu="$(bounded "$probe_timeout" awk '/^cpu /{print "user="$2" nice="$3" system="$4" idle="$5" iowait="$6" irq="$7" softirq="$8" steal="$9}' "$proc_root/stat" 2>/dev/null || true)"; [ -n "$cpu" ] || cpu=unavailable
mem="$(bounded "$probe_timeout" sh -c 'free -m 2>/dev/null' | awk '/^Mem:/{print "total="$2" used="$3" free="$4" available="$7} /^Swap:/{print "swap_total="$2" swap_used="$3" swap_free="$4"}' || true)"; [ -n "$mem" ] || mem=unavailable
psi="$(bounded "$probe_timeout" awk 'NF{printf "%s ",$0}' "$proc_root/pressure/memory" 2>/dev/null || true)"; [ -n "$psi" ] || psi=unavailable
cap="$(bounded "$probe_timeout" sh -c 'df -B1 / 2>/dev/null' | tail -n +2 | tr '\n' ';' || true)"; [ -n "$cap" ] || cap=unavailable
line="$(date -u +%FT%TZ) cpu[$cpu] mem[$mem] psi_memory[$psi] fs_capacity[$cap]"
fi
[ -n "$line" ] || line="$(date -u +%FT%TZ) unavailable"
bytes="$(printf '%s\n' "$line" | wc -c | tr -d ' ')"
if [ $((total + bytes)) -gt "$cap_bytes" ]; then
banner='size cap reached'; bb="$(printf '%s\n' "$banner" | wc -c | tr -d ' ')"
[ $((total + bb)) -le "$cap_bytes" ] && printf '%s\n' "$banner" >> "$log"
break
fi
printf '%s\n' "$line" >> "$log"; total=$((total + bytes))
remaining=$((end - $($now))); [ "$remaining" -gt 0 ] || break
step="$interval"; [ "$step" -lt "$remaining" ] || step="$remaining"
"$nap" "$step"
done- Optional directory growth (bounded, opt-in): only
du -sb --max-depth=0on the worktree and~/.t3/userdata. Do not recursively scan user homes or archives. - Heavy-job activity: recording start/stop of two deliberate heavy jobs at once is a pilot choice, not measured capacity.
- Session continuity: record reconnects, and note that a missing native session now surfaces as an error instead of a silent fresh thread.
- Never collect prompts, tool arguments, credentials, or raw session content.
- Independent review and merge of the source PR, then a release containing the fix (T3 release owner).
- Target/account confirmation and access preparation (ENV-1); this page records UNKNOWN.
- Missing OpenCode/provider/host receipts from §2.
- Real target verification, dormant install, and later activation remain unexecuted here.