# ROBOT — hub-and-spoke trading system · HANDOFF for the next session (any local box)

Written 2026-08-28 00:10 KST at the end of the build session. Live copy:
`https://bitcoin-fund-manager.com/apps2/robot/HANDOFF.md` · rendered: `/apps2/robot/`.
Read this first, then `ARCHITECTURE-HUB-SPOKE.md` (same folder — the binding API contract).

---

## 0. The owner's orders this system answers (verbatim, 2026-08-27)

- ~16:30 KST — *"design the trading system so that all wallets stay local and all connections to
  ostium happen on local machine. vpn is nyc based on ip blocked. architect a system for multiple
  users to duplicate a local original setup with their own unique wallets. also include a tracking
  system that stores pnl from each local wallet in the vpn … hub and spokes … accounting system that
  requires all spokes to send x% profits to hub based wallet to continue having access to trading
  logic … Before each trade, app will call to timeportal api and the api will say something like:
  pause longs today. or pause shorts today. or pause everything today. Without the timeportal.pro
  algo, the current auto trading system will just trade all sessions with bullish bias as longs and
  all sessions with bearish bias as shorts. Design and build"*
- ~17:05 KST — *"build at bitcoin-fund-manager.com/robot"*
- ~17:20 KST — *"when done, write work summary in bitcoin-fund-manager.com/apps2/robot for next
  session from different local box to pick up and continue"* (this file)

## 1. What exists now (all live, all tested)

| piece | where | state |
|---|---|---|
| **Hub API** | `https://bitcoin-fund-manager.com/robot/api.php?op=…` (VPS `/var/www/bitcoin-fund-manager.com/robot/`) | v1.0.0 live · PHP 8.3 + SQLite · 206/0 assertions (`php tests.php`, CLI only) |
| **Owner console** | `https://bitcoin-fund-manager.com/robot/` | Spokes · Advice today · Ledger · Tribute · Settings — paste the admin token once |
| **Hub DB** | VPS `/var/lib/tp-hub/hub.sqlite` (WAL, `600`, outside every docroot) | 10 tables per spec §6; 0 spokes enrolled |
| **Admin token** | VPS `/var/lib/tp-hub/admin-token` (`600 claude:claude`) | copy on the build box: `D:\work\trade\hub\ADMIN-TOKEN.local.txt` — on a new box read it over ssh: `ssh timeportal cat /var/lib/tp-hub/admin-token` |
| **Spoke client** | git `timeportal:/var/git/trade.git` → `spoke/` (Python 3.10–3.13, `ostium-python-sdk` 3.3.1) | 96 tests / 348 assertions green; paper mode proved end-to-end against the live hub |
| **Spec** | `trade.git:ARCHITECTURE-HUB-SPOKE.md` (also in this folder) | §6a/§6b record the two implementation deviations |
| **Snapshots** | `/var/git/bfm-robot.git` (worktree `/robot`, head `f466b54`) · `/var/git/trade.git` (spoke + hub mirror + spec) | house pattern: git dirs outside docroots |
| **Ledger** | `apps/USER-DEMANDS-LEDGER.md` → "Session 2026-08-27" | orders logged verbatim |

**Settings (live):** `tribute_pct=10` · `period=weekly` (ET, Fri 16:00 close) · `grace_days=3` ·
**`enforce=0` (OFF)** · `hub_wallet` **unset** (set it in Settings before any tribute can be verified).

**Proved on 2026-08-27 (throwaway spoke, deleted after):** wallet create → enrol → approve →
claim → preflight 11/12 PASS → `run --mode paper --once --at 09:50` opened 3 windows from the
real plan (CRCL/ORCL/META shorts) → `--at 11:00` closed 3 → hub ledger shows the 3 fills, net of the
cost model (−$5.42 on flat prices = fees + slippage, as intended) → `tribute status` reads back.

## 2. How the pieces fit (30-second version)

```
spoke (local, non-US VPN, own wallet)  ──HMAC──▶  hub /robot (VPS, US IP, no keys, never touches Ostium)
   plan   ◀── the GATE: no plan, no trades (403 when tribute overdue AND enforce=on)
   advice ◀── the FILTER: pause_all / pause_longs / pause_shorts / per-ticker; fail-open to "no pauses"
   report ──▶ every fill (idempotent event_id) → ledger → weekly HWM tribute → access state
   tribute_submit ──▶ hub verifies the USDC Transfer log on Arbitrum (spoke wallet → hub_wallet)
spoke ──ostium-python-sdk──▶ Ostium (Arbitrum One live / Sepolia testnet)   ← only ever from a spoke
```
Default behaviour with no advice set = the owner's rule: every bullish-bias window long, every
bearish-bias window short (55/45 gate from `plan.rules`). Plan source v1 = `apps2/ostium/data/snapshot.json`.

## 3. Pick up on a DIFFERENT local box — exact steps

```powershell
# 0. prerequisites: git, Python 3.10–3.13 (NOT 3.14 — web3 has no wheels), ssh key for `timeportal`
#    (~/.ssh/config Host timeportal → root@167.71.169.206; key = ~/.ssh/id_ed25519)
git clone timeportal:/var/git/trade.git D:\work\trade
cd D:\work\trade\spoke
.\setup.ps1                              # venv + pip install + copies config.example.yaml → config.yaml
python -m spoke wallet create            # YOUR key, YOUR passphrase — never leaves this box
python -m spoke enrol --label "<name>"   # prints spoke_id; owner approves in https://bitcoin-fund-manager.com/robot/
python -m spoke claim --wait 600         # receives the API key once → state/spoke.json
python -m spoke preflight                # must end PREFLIGHT OK (VPN country must not be US)
python -m spoke run --mode paper         # ET-clock loop; --once --at 09:50 for a dry pass
```
Claude continuity on the new box: `ssh timeportal 'cd /home/claude/CONTINUITY && tar czf - current' | tar xzf -`
gives the memory kit (249 files) + `agents/chart-builder.md` (the HARD chart laws) + the master
ledger. House rules for BFM apps are in the docroot `apps/*.md`, `apps2/*.md` — pull them too.

## 4. Open gates — nothing below has been done, on purpose

1. **No real wallet exists yet.** The owner creates spoke #1's wallet with a passphrase only they know.
2. **`hub_wallet` unset** → set in console Settings (the address that receives tribute).
3. **No testnet order has been placed.** Next proof step: `mode: testnet`, fund Sepolia USDC via
   `sdk.faucet`, run one window, confirm the fill lands in the hub ledger with `mode=testnet`.
4. **Live is refused** until `spoke/state/APPROVED-MONEY-RULES.stamp` exists (owner creates by
   hand after approving `MONEY-RULES.md` O1–O25 + §11.4) **and** the Opus adversarial audit is
   recorded. Settled money rules the code enforces: $10 base stake · 100× capped by venue pair ·
   55/45 gate · one-shot 1.5× after 3 same-(ticker,side) losses then back to base · max 3 open ·
   one per ticker · max daily loss $100 · day-trades closed 15:45 ET · stocks 09:31–15:59 ET.
5. **The pause algo is not built** — `advice` is whatever the owner sets in the console each day
   (three switches + per-ticker). The algo's job later is to write the same `advice` row.
6. **`enforce` is OFF** — tribute is computed and shown but nobody is cut off yet.
7. **Auth phase 2** (EIP-191 wallet-signature login) deferred: needs `php8.3-gmp` + a vendored
   secp256k1 on the VPS. Today: per-spoke API key + HMAC (hub stores the key — HMAC is symmetric).
8. **Plan source** is still the ostium snapshot produced by the old autotrader
   (`D:\work\trade\apps\autotrader` on the other box — NOT in trade.git). Growth step: hub computes
   plans itself from hourality's engine output.
9. Second venue (Hyperliquid HIP-3 equity perps trade 24/7 — measured 2026-08-27) → `spoke/venue/`.

## 5. Gotchas measured this session

- Ostium's price feed + subgraph need no key → paper fills at the real venue price.
- Two byte-identical signed requests in the same second collide as a replay → the spoke never
  reuses a (ts, sig) pair; do not "fix" this by adding a nonce (contract stays as-is).
- `fills.pnl_usd` is **net**; `fees_usd` is informational — never subtract twice.
- Windows has no IANA tz database → `tzdata` is in requirements; every rule is ET.
- `--at` accepts `2026-08-27T09:50` or bare `09:50` (today ET).
- No `sqlite3` CLI on the VPS — backup via `php -r` `VACUUM INTO` (hub README).
- `quantcharts.pro` shares the docroot, so `/robot` is reachable there too.

## 6. Other work shipped the same session (for context)

- `apps2/` hub: 5-line tile clamp + click-to-expand; nested-anchor tile corruption fixed (`5530a2d`).
- `apps2/hourality/futures` + parent: frame/duration in the title bar, block clock range in tooltips,
  totalled multi-column selection, `$1` bull/bear/AM/PM what-ifs in the plays table, net-per-bar
  tick (`6a98075`; suites 401/157/709/229 green).
- Ostium app status: control plane + TOTP shipped but never enrolled; paper loop never run; see
  the ledger entry for the defect list.
