fix(cli)(#48): wrapper field-trial fixes — 3-shot bootstrap, UNICITY_ env strip, identical-path bind, post-spawn SET_STRATEGY#50
Open
vrogojin wants to merge 1 commit into
Open
Conversation
…rip soak
Surfaced by running the trader-roundtrip soak end-to-end. Three real
bugs in the original sphere-cli#48 wrapper that field-testing exposed:
1. Two-shot bootstrap was a 3-shot dance — `parseDriftError` derived
`managerDirectAddress` from `loadedPubkey` assuming
`DIRECT://<pubkey>`, but sphere wallets compute a 36-byte
`DIRECT://0000…` address that's NOT the pubkey. Second boot now
fails with MANAGER_DIRECT_ADDRESS mismatch. Fix: loop up to 3 boots,
reading whichever mismatch the HM surfaces (pubkey on boot 1,
direct-address on boot 2), succeed on boot 3.
2. HM rejects all `UNICITY_*` keys in user-supplied env (HM blocks the
prefix to prevent boot-envelope spoofing — see
agentic_hosting/src/shared/forbidden-env.ts). The wrapper was
sending UNICITY_CONTROLLER_PUBKEY, UNICITY_NETWORK,
UNICITY_TRUSTED_ESCROWS — all rejected. Fixes:
* Strip every UNICITY_* from `buildTraderEnv`. The HM auto-injects
CONTROLLER_PUBKEY (from request sender), NETWORK, BOOT_TOKEN,
INSTANCE_ID/NAME/TEMPLATE_ID, DATA_DIR, TOKENS_DIR itself.
* `--trusted-escrows` chains to a post-spawn SET_STRATEGY ACP
command. Best-effort: on failure, instructs the operator how to
re-apply manually.
3. Docker-in-docker bind-mount trap on the tenants dir. The HM
bind-mounts `${TENANTS_DIR}/<instance>/wallet` into each tenant via
the shared docker socket; the daemon resolves the source path on
the HOST. If TENANTS_DIR is a container-only path, the host doesn't
find it, docker creates an empty root:root dir at the missing host
path, and the tenant's `node` user (uid 1000) hits EACCES. Fix:
bind the host tenants dir into the HM at the IDENTICAL host path,
and set `TENANTS_DIR` env to that same path. Matches the production
`agentic_hosting/docker/docker-compose.override.yml` pattern.
Also:
- Trader template image bumped v0.1 → v0.2 (the rebuilt image that
carries sphere-sdk#456/#457/#464/#447).
- Spawn payload defaults `nametag: instance_name` — the trader-service
ignores this and derives its own `t-<hex>` from the instance ID, but
the field is harmless metadata and matches existing host-spawn UX.
187 unit tests pass (29 in local-hm.test.ts including parse-drift
both-shapes, identical-path-bind assertions; 13 in spawn.test.ts
including the no-UNICITY-prefix defense-in-depth check).
Verified end-to-end against testnet through §5 of
sphere-sdk#475's `manual-test-trader-roundtrip.sh`:
§1 wallets created ✓
§2 faucet (local-mint) ✓
§2.5 escrow + market ping ✓
§3 per-user HM + tenants ✓ (3-shot bootstrap + identical-path bind)
§4 ACP probe ✓
§5 deposits confirmed ✓
§6 intent post ✗ (market-api HTTP 500 — server-side
rejection of trader's price=1e17
payload, unrelated to wrapper)
Follow-up sphere-cli#48 wrapper: defaults templates to trader:v0.2
match agentic_hosting templates.json (PR #27); needs another bump
when v0.3 lands.
vrogojin
added a commit
to unicity-sphere/sphere-sdk
that referenced
this pull request
Jun 11, 2026
…rough (#482) Working end-to-end through §5 of the trader-roundtrip soak surfaced three CLI-form mismatches in the script: 1. `extract_chain_pubkey` looked for `chainPubkey:` (camelCase, the `sphere init` output shape) but `sphere status` emits `Chain Pubkey:` (space-separated, human-label shape). The regex matched neither, the extract function returned empty, and `[[ -n $ALICE_PUBKEY ]]` failed the script at line ~580. Fix: case-insensitive regex with optional internal whitespace `(chain[[:space:]]*pubkey)` matches both output formats. 2. §3 hardcoded the tenant nametag as the instance name (`$ALICE_TRADER_TAG = alice-trader-$SUFFIX`). The trader-service actually derives its own nametag as `t-<18-hex>` from the instance ID (see trader-service/src/trader/main.ts:279). Result: every downstream `sphere trader ... --tenant '@$ALICE_TRADER_TAG'` landed at the wrong nametag. Fix: parse the live `tenant_direct_address` from `sphere trader spawn --json` output and re-bind `ALICE_TRADER_TAG` to that value (sans leading `@`). All existing downstream usages then resolve correctly. 3. §5 deposit step had the args in the wrong order — `sphere payments send 50 UCT --to "@$ALICE_TRADER_TAG"` — but the current CLI surface is `sphere payments send <recipient> <amount> <coin>` (recipient first, no `--to` flag). Fix: swap args to match CLI. Verified end-to-end against testnet (with sphere-cli#50 follow-up wrapper fixes applied): §1 wallets created ✓ §2 faucet (local-mint) ✓ §2.5 escrow + market pre-flight ✓ §3 per-user HM + trader spawn ✓ §4 ACP probe ✓ §5 deposits confirmed ✓ §6 intent post ✗ (market-api HTTP 500 — out of scope here; price=1e17 payload rejected at the api server) Related: unicity-sphere/sphere-cli#50 (wrapper follow-up fixes that this soak run surfaced).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Field-trial fixes for the `sphere trader spawn` wrapper that landed in #49 (closes #48). Three real bugs surfaced when running the trader-roundtrip soak end-to-end:
Two-shot bootstrap was a 3-shot dance. `parseDriftError` derived `managerDirectAddress` from `loadedPubkey` assuming `DIRECT://` — but sphere wallets compute a 36-byte `DIRECT://0000…` address that's NOT the pubkey. Boot 2 failed with MANAGER_DIRECT_ADDRESS mismatch. Fix: loop up to 3 boots, reading whichever mismatch the HM surfaces each time (pubkey on boot 1, direct-address on boot 2), succeed on boot 3.
HM blocks the entire `UNICITY_*` prefix in user-supplied env (`agentic_hosting/src/shared/forbidden-env.ts`) to prevent boot-envelope spoofing. The wrapper was sending `UNICITY_CONTROLLER_PUBKEY`, `UNICITY_NETWORK`, `UNICITY_TRUSTED_ESCROWS` — all rejected with `Forbidden env var prefix in payload.env`. Fix:
Docker-in-docker bind-mount trap on the tenants dir. The HM bind-mounts `${TENANTS_DIR}//wallet` into each tenant via the shared docker socket. The daemon resolves the source path on the HOST. If `TENANTS_DIR` is a container-only path, the host doesn't find it, docker creates an empty root:root dir at the missing host path, and the tenant's `node` user (uid 1000) hits EACCES. Fix: bind the host tenants dir into the HM at the IDENTICAL host path, set `TENANTS_DIR` env to that same path. Matches the production agentic_hosting/docker/docker-compose.override.yml pattern.
Also
Test plan
Related