Skip to content

Commit 673fffa

Browse files
authored
Merge pull request #9 from ProjectOpenSea/docs/bootstrap-consolidate-phases
docs(bootstrap): collapse setup into 1 Pinata restart + 1 owner ceremony
2 parents 1eba8a5 + e11ed90 commit 673fffa

3 files changed

Lines changed: 91 additions & 79 deletions

File tree

workspace/BOOTSTRAP.md

Lines changed: 86 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,27 @@ There is no memory yet on a totally fresh deploy. That's normal: `memory/` and m
1313

1414
## Helper: locate the Pinata Platform CLI
1515

16-
Phases 0, 1, and 2 use the bundled Pinata Platform skill to attach secrets and restart. Find it once at the top of any session that needs it:
16+
Phase 0 uses the bundled Pinata Platform skill to attach secrets and restart. Find it once at the top of any session that needs it:
1717

1818
```bash
1919
PINATA_CLI=$(find skills -path '*pinata*platform*/cli.mjs' 2>/dev/null | head -1)
2020
```
2121

2222
If `PINATA_CLI` is empty, the skill didn't mount. Tell the user, point at `manifest.json``skills`, and stop.
2323

24-
## Phase 0 — Auto-provision the OpenSea API key
24+
## Phase 0 — Provision all secrets in one restart
25+
26+
**Skip if:** all of `OPENSEA_API_KEY`, `PRIVY_APP_ID`, `PRIVY_APP_SECRET`, `PRIVY_WALLET_ID`, `PRIVY_AUTH_SIGNING_KEY` are set.
27+
28+
This phase batches every secret-attach into a single Pinata restart. Each `restart` ends the conversation and forces a cold resume, so the goal here is to make the user paste their Privy credentials exactly once, do all the auto-provisioning in the same shell session using inline env, then attach everything and restart together.
29+
30+
Walk through 0a–0d in order. Each sub-step has its own skip check so partial state from a previous attempt resumes cleanly.
31+
32+
### 0a — OPENSEA_API_KEY
2533

2634
**Skip if:** `OPENSEA_API_KEY` is set.
2735

28-
Otherwise:
36+
Otherwise auto-fetch a free-tier instant key:
2937

3038
```bash
3139
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://api.opensea.io/api/v2/auth/keys)
@@ -37,139 +45,143 @@ if [ "$STATUS" = "429" ]; then
3745
exit 1
3846
fi
3947

40-
KEY=$(printf '%s' "$BODY" | jq -r .api_key)
41-
[ -n "$KEY" ] && [ "$KEY" != "null" ] || { echo "instant key fetch failed (status $STATUS): $BODY"; exit 1; }
42-
43-
node "$PINATA_CLI" create-secret OPENSEA_API_KEY "$KEY" --attach
44-
```
45-
46-
Then tell the user:
47-
48-
> "I've fetched a free-tier OpenSea API key and attached it. Restarting now — I'll come back as a fresh session (no chat memory) and pick up from `IDENTITY.md` + env."
49-
50-
Then:
51-
52-
```bash
53-
node "$PINATA_CLI" restart
48+
OPENSEA_API_KEY=$(printf '%s' "$BODY" | jq -r .api_key)
49+
[ -n "$OPENSEA_API_KEY" ] && [ "$OPENSEA_API_KEY" != "null" ] || { echo "instant key fetch failed (status $STATUS): $BODY"; exit 1; }
5450
```
5551

56-
Stop. The conversation ends here; you'll resume cold and the env will have `OPENSEA_API_KEY`.
52+
Hold the value in the shell var. Don't `--attach` yet — that happens at the end of the phase in 0d.
5753

58-
## Phase 1 — Collect Privy app credentials
54+
### 0b — PRIVY_APP_ID + PRIVY_APP_SECRET
5955

6056
**Skip if:** both `PRIVY_APP_ID` and `PRIVY_APP_SECRET` are set.
6157

62-
Otherwise, ask the user:
63-
64-
> "I need a Privy application before I can create your wallet. Create one at https://dashboard.privy.io (free tier is fine), then either paste your App ID and App Secret here and I'll attach them, or add them via Pinata's env UI yourself and restart."
58+
Otherwise, ask the user once:
6559

66-
If the user pastes them in chat:
67-
68-
```bash
69-
node "$PINATA_CLI" create-secret PRIVY_APP_ID "$ID" --attach
70-
node "$PINATA_CLI" create-secret PRIVY_APP_SECRET "$SECRET" --attach
71-
node "$PINATA_CLI" restart
72-
```
60+
> "I need a Privy application before I can create your wallet. Create one at https://dashboard.privy.io (free tier is fine), then paste your **App ID** and **App Secret** here. I'll create the wallet, generate my own signer, and attach all the secrets in a single batch — only one restart at the end of this phase."
7361
74-
Stop. The agent comes back as a fresh session and resumes at Phase 2.
62+
When the user pastes them, capture into shell vars (`PRIVY_APP_ID=...`, `PRIVY_APP_SECRET=...`). Do not echo `PRIVY_APP_SECRET` back to chat or to logs.
7563

76-
If the user adds them via Pinata UI directly, ask them to restart from the UI — same outcome.
64+
If the user prefers to add them via Pinata's env UI directly, ask them to do that and restart from the UI. After restart, this sub-step will see them in env and skip.
7765

78-
## Phase 2 — Auto-provision wallet ID + additional_signer keypair
66+
### 0c — Wallet ID + additional_signer keypair
7967

8068
**Skip if:** both `PRIVY_WALLET_ID` and `PRIVY_AUTH_SIGNING_KEY` are set.
8169

82-
**Precondition:** `PRIVY_APP_ID` + `PRIVY_APP_SECRET` present.
70+
**Precondition:** `PRIVY_APP_ID` and `PRIVY_APP_SECRET` are present, either in env or in shell vars from 0b.
8371

8472
Generate the agent's additional_signer keypair (pure-local, no API call):
8573

8674
```bash
87-
opensea wallet generate-auth-key --format json
75+
KEYPAIR=$(opensea wallet generate-auth-key --format json)
76+
PRIVY_AUTH_SIGNING_KEY=$(printf '%s' "$KEYPAIR" | jq -r .privateKey)
77+
ADDITIONAL_SIGNER_PUBKEY=$(printf '%s' "$KEYPAIR" | jq -r .publicKey)
8878
```
8979

90-
Capture `privateKey` (PKCS8 base64) and `publicKey` (SPKI base64) from the output. Do **not** echo `privateKey` to the chat or to logs — pass it directly to Pinata in the next step.
80+
Do **not** echo `PRIVY_AUTH_SIGNING_KEY` to chat or logs.
9181

92-
Create the wallet without an owner (it will be hardened in Phase 3):
82+
Create the wallet without an owner (it will be hardened in Phase 1). Pass the Privy creds **inline** as subprocess env so this works in the same shell session that just collected them — no restart needed before the call:
9383

9484
```bash
95-
opensea wallet create --chain-type ethereum --format json
85+
WALLET_JSON=$(PRIVY_APP_ID="$PRIVY_APP_ID" PRIVY_APP_SECRET="$PRIVY_APP_SECRET" \
86+
opensea wallet create --chain-type ethereum --format json)
87+
PRIVY_WALLET_ID=$(printf '%s' "$WALLET_JSON" | jq -r .id)
88+
WALLET_ADDRESS=$(printf '%s' "$WALLET_JSON" | jq -r .address)
9689
```
9790

98-
Capture the wallet `id` from the output. The CLI will print a loud `WARNING: created without --owner-public-key` to stderr — that's expected; we register the owner in the off-machine ceremony in Phase 3. The brief unhardened window between wallet creation and owner registration is acceptable because the wallet has zero balance and no policy attached during this window — there is nothing to lose if the credentials in env were misused. Phase 3 refuses to advance to any signing-capable step until owner gating is verified.
99-
100-
Attach both secrets:
91+
The CLI will print a loud `WARNING: created without --owner-public-key` to stderr — that's expected; we register the owner and attach the spend policy together in the off-machine ceremony in Phase 1. The brief unhardened window between wallet creation and that ceremony is acceptable because the wallet has zero balance and no policy attached during this window — there is nothing to lose if the credentials in env were misused. Phase 1 refuses to advance until owner gating AND policy are both confirmed.
10192

102-
```bash
103-
node "$PINATA_CLI" create-secret PRIVY_WALLET_ID "$ID" --attach
104-
node "$PINATA_CLI" create-secret PRIVY_AUTH_SIGNING_KEY "$PRIVATE_KEY" --attach
105-
```
93+
If `opensea wallet create` fails with an auth error, the Privy credentials the user pasted are wrong. Tell the user, ask them to recheck the dashboard, and re-collect 0b. **Don't proceed to 0d** with bad creds — you'd attach them, restart, and discover the failure cold next session.
10694

10795
Record in `IDENTITY.md` under `## Wallet`:
10896

109-
- `Address: <address>`
110-
- `Wallet ID: <id>`
111-
- `Additional-signer pubkey (SPKI base64): <publicKey>` — Phase 3 needs to re-show this to the user across restarts.
97+
- `Address: <WALLET_ADDRESS>`
98+
- `Wallet ID: <PRIVY_WALLET_ID>`
99+
- `Additional-signer pubkey (SPKI base64): <ADDITIONAL_SIGNER_PUBKEY>` — Phase 1 needs to re-show this to the user across restarts.
112100
- `hardening_status: pending_owner_registration`
113101

102+
### 0d — Attach everything and restart once
103+
104+
Attach each secret you provisioned in 0a–0c (skip the line for any secret that was already in env coming into this phase — those are already persisted):
105+
106+
```bash
107+
node "$PINATA_CLI" create-secret OPENSEA_API_KEY "$OPENSEA_API_KEY" --attach
108+
node "$PINATA_CLI" create-secret PRIVY_APP_ID "$PRIVY_APP_ID" --attach
109+
node "$PINATA_CLI" create-secret PRIVY_APP_SECRET "$PRIVY_APP_SECRET" --attach
110+
node "$PINATA_CLI" create-secret PRIVY_WALLET_ID "$PRIVY_WALLET_ID" --attach
111+
node "$PINATA_CLI" create-secret PRIVY_AUTH_SIGNING_KEY "$PRIVY_AUTH_SIGNING_KEY" --attach
112+
```
113+
114114
Tell the user what just happened, then restart:
115115

116-
> "Wallet `<address>` created. I generated my own additional_signer keypair and stored the private half as `PRIVY_AUTH_SIGNING_KEY`. Restarting now — I'll come back as a fresh session and walk you through the one-time off-machine ceremony to register an owner key."
116+
> "All set: OpenSea key fetched, Privy app credentials saved, wallet `<WALLET_ADDRESS>` created, and my additional_signer keypair generated (private half stored as `PRIVY_AUTH_SIGNING_KEY`). Restarting now — I'll come back as a fresh session and walk you through the one-time off-machine ceremony to register your owner key and attach a spend policy."
117117
118118
```bash
119119
node "$PINATA_CLI" restart
120120
```
121121

122-
Stop. Resume cold from Phase 3.
122+
Stop. The conversation ends here; you'll resume cold from Phase 1.
123123

124-
## Phase 3Verify hardening (user off-machine ceremony)
124+
## Phase 1One-time off-machine ceremony (owner + signer + policy)
125125

126-
**Skip if:** `IDENTITY.md` says `hardening_status: confirmed`.
126+
**Skip if:** `IDENTITY.md` says `hardening_status: confirmed` AND `opensea wallet info` shows `policyIds.length > 0`.
127127

128128
**Precondition:** `PRIVY_WALLET_ID` + `PRIVY_AUTH_SIGNING_KEY` set.
129129

130+
This phase bundles the three off-machine actions that all require the user's owner private key into one focused session, so the user only has to bring out their owner key once.
131+
130132
Check current posture:
131133

132134
```bash
133135
opensea wallet info --format json
134136
```
135137

136-
If the output shows `ownerEnforcesAuthKey: true` AND `additionalSignerCount >= 1`, hardening is complete. Update `IDENTITY.md`:
138+
The three properties to verify:
139+
140+
- `ownerEnforcesAuthKey: true` — owner key registered
141+
- `additionalSignerCount >= 1` — agent's signer registered
142+
- `policyIds.length > 0` — spend policy attached
143+
144+
If all three pass, the ceremony is complete. Update `IDENTITY.md`:
137145

138146
- `hardening_status: confirmed`
139147
- `auth_key_gating: yes`
148+
- Policy template: *Agent Trading — Conservative*
149+
- Per-tx cap: `<cap from earlier conversation, if known — otherwise note "see policy"`>
140150

141-
Then advance to Phase 4.
151+
Then advance to Phase 2.
142152

143-
Otherwise (most likely on first arrival here), tell the user:
153+
Otherwise (most likely on first arrival here), walk the user through the combined ceremony. First, settle the per-tx cap conversationally so they can plug it into the policy template before going off-machine:
144154

145-
> "Wallet is at `<address>` (id `<wallet_id>`). Before I can sign anything, two things need to happen on YOUR machine — never here:
155+
1. Ask them their per-tx cap in ETH (no example — they choose). Convert to wei: `0.05 ETH = 50000000000000000 wei`.
156+
2. Show them the *Agent Trading — Conservative* template from `skills/opensea/references/wallet-policies.md`, with their cap substituted into the `value lte` rule. Use the chain allowlist and Seaport destination from the same reference, verbatim.
157+
158+
Then tell them:
159+
160+
> "Wallet is at `<address>` (id `<wallet_id>`). Before I can sign anything, three things need to happen on YOUR machine — never here. Get them all out of the way in one session with your owner key:
161+
>
162+
> 1. **Generate your owner keypair locally.** Easiest path: install the OpenSea CLI locally (`npm install -g @opensea/cli`) and run `opensea wallet generate-auth-key`. Or use any P-256 keygen (e.g. `openssl ecparam -name prime256v1 -genkey -noout -out owner.pem` and convert to SPKI base64). Keep the private key on your machine — never paste it to me, never put it in env, never check it into anything.
146163
>
147-
> 1. Generate your own owner keypair. Easiest path: install the OpenSea CLI locally (`npm install -g @opensea/cli`) and run `opensea wallet generate-auth-key`. Or use any P-256 keygen (e.g. `openssl ecparam -name prime256v1 -genkey -noout -out owner.pem` and convert to SPKI base64). Keep the private key on your machine — never paste it to me, never put it in env, never check it into anything.
164+
> 2. **Register two keys on the wallet:** your owner public key as `owner_id`, AND my additional_signer public key (`<additional_signer_pubkey from IDENTITY.md>`) as a signer.
148165
>
149-
> 2. Register both keys on the wallet: your owner public key as `owner_id`, AND my additional_signer public key (`<additional_signer_pubkey from IDENTITY.md>`) as a signer. Both via the Node script in https://github.com/ProjectOpenSea/opensea-skill/blob/main/docs/policy-administration.md.
166+
> 3. **Attach the spend policy** I just showed you (with your `<cap>` ETH cap), so the per-tx ceiling is enforced in Privy's TEE.
167+
>
168+
> Steps 2 and 3 are both done with the Node script in https://github.com/ProjectOpenSea/opensea-skill/blob/main/docs/policy-administration.md, signed by your owner key (off-machine). Do them in order: register first, attach policy second — once owner gating is on, the policy attach also requires the owner signature.
150169
>
151170
> After this one-time ceremony, the day-to-day is asymmetric: I sign every trade with my additional_signer key (lives in env, scoped to `/rpc` only). Your owner key only comes out when you decide to change the policy itself — raise the cap, edit the chain allowlist, etc. You don't need to be online with the owner key for me to trade.
152171
>
153-
> Once both keys are registered, type 'done' and I'll verify."
154-
155-
When user says done, re-run `opensea wallet info`. If the checks pass, record `hardening_status: confirmed` and advance. If not, surface the actual `ownerEnforcesAuthKey` and `additionalSignerCount` values from the JSON and ask the user to confirm both off-machine steps completed.
172+
> Once all three are done, type 'done' and I'll verify."
156173
157-
**Refuse to advance to Phase 4 until hardening is confirmed.** Do not proceed to spend-policy attachment, balance checks, watchlist calibration, or any signing-capable operation while `ownerEnforcesAuthKey` is false. If the user pushes back, explain: without owner gating, the credentials in env can rewrite the policy that's supposed to constrain spending — there's no real ceiling.
174+
When the user says done, re-run `opensea wallet info`. Verify each property and surface specifically which (if any) failed:
158175

159-
## Phase 4 — Apply spend policy (user off-machine)
176+
- `ownerEnforcesAuthKey: false` → owner registration didn't take. Re-do step 2.
177+
- `additionalSignerCount < 1` → signer registration didn't take. Re-do step 2.
178+
- `policyIds.length == 0` → policy attach didn't take. Re-do step 3.
160179

161-
**Skip if:** `opensea wallet info` shows `policyIds.length > 0`.
180+
Don't advance with partial state. Once all three pass, record the IDENTITY.md fields above and advance to Phase 2.
162181

163-
**Precondition:** `hardening_status: confirmed`.
164-
165-
Walk the user through choosing and applying a per-tx policy:
166-
167-
1. Ask them their per-tx cap in ETH (no example — they choose). Convert to wei: `0.05 ETH = 50000000000000000 wei`.
168-
2. Show them the *Agent Trading — Conservative* template from `skills/opensea/references/wallet-policies.md`, with their cap substituted into the `value lte` rule. Use the chain allowlist and Seaport destination from the same reference, verbatim.
169-
3. Tell them to attach it via the Node script in https://github.com/ProjectOpenSea/opensea-skill/blob/main/docs/policy-administration.md. They'll need their owner private key (off-machine) to sign the request. Do NOT construct or run the `PATCH` request from here.
170-
4. When they say done, re-run `opensea wallet info`. Confirm `policyIds.length > 0`. Record the policy template and per-tx cap in `IDENTITY.md`.
182+
**Refuse to advance to Phase 2 (funding) until all three pass.** Without owner gating, the credentials in env can rewrite the policy that's supposed to constrain spending. Without a policy, there is no per-tx ceiling. Funding before either is in place creates a window where env credentials could drain the wallet. If the user pushes back, explain the order and offer to keep waiting.
171183

172-
## Phase 5 — Hot-wallet funding
184+
## Phase 2 — Hot-wallet funding
173185

174186
**Skip if:** `IDENTITY.md` already has a `float_per_chain` entry.
175187

@@ -187,7 +199,7 @@ Record in `IDENTITY.md` under `## Wallet`:
187199

188200
For each funded chain, do a quick balance check (use the native-balance path from `skills/opensea/references/wallet-setup.md` or `eth_getBalance` via a public RPC). If a target chain shows 0, note it — you can still track floors there, you just can't buy.
189201

190-
## Phase 6 — Conversation, identity, watchlist
202+
## Phase 3 — Conversation, identity, watchlist
191203

192204
Don't interrogate. Just talk. Start with something like:
193205

@@ -214,7 +226,7 @@ Update:
214226
- `USER.md` — their name, timezone, collector profile, risk tolerance, per-tx comfort.
215227
- `MEMORY.md` (create it in `workspace/`) — seed with any taste signals from the opening conversation.
216228

217-
## Phase 7 — Cleanup
229+
## Phase 4 — Cleanup
218230

219231
Delete this file. You don't need a bootstrap script anymore — you're you now.
220232

workspace/IDENTITY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
- **Policy template:**
1616
- **Spending limit (per tx):** _(ETH / wei — record both for clarity, e.g. "0.05 ETH / 50000000000000000 wei")_
1717
- **hardening_status:** _(pending_creation | pending_owner_registration | pending_policy | confirmed — used by BOOTSTRAP for resumability across restarts)_
18-
- **additional_signer_pubkey:** _(SPKI base64; the public half of `PRIVY_AUTH_SIGNING_KEY`. Persisted so BOOTSTRAP Phase 3 can re-show it to the user across cold restarts.)_
19-
- **auth_key_gating:** _(yes | no — captured from `opensea wallet info` after Phase 3 verifies `ownerEnforcesAuthKey: true`)_
18+
- **additional_signer_pubkey:** _(SPKI base64; the public half of `PRIVY_AUTH_SIGNING_KEY`. Persisted so BOOTSTRAP Phase 1 can re-show it to the user across cold restarts.)_
19+
- **auth_key_gating:** _(yes | no — captured from `opensea wallet info` after Phase 1 verifies `ownerEnforcesAuthKey: true`)_
2020
- **float_per_chain:** _(mirrored from `TOOLS.md``floatPerChain`; the real aggregate ceiling)_
2121
- **funding_wallet:** _(optional — user's cold/funding wallet address, so the agent can recognize replenishment events in `events by-account` streams)_

0 commit comments

Comments
 (0)