@dev.sail.money/sailor 0.1.0-local → 1.0.0-38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/AGENTS.md +139 -140
  2. package/LICENSE +21 -21
  3. package/README.md +428 -430
  4. package/docs/PERMISSION_MODEL.md +93 -93
  5. package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -179
  6. package/examples/permissions/BoundedBet_Limitless_Base.sol +97 -97
  7. package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +94 -94
  8. package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +154 -154
  9. package/examples/permissions/BoundedStake_Venice_Base.sol +85 -85
  10. package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +82 -82
  11. package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +116 -116
  12. package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +150 -150
  13. package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +73 -73
  14. package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -97
  15. package/examples/permissions/README.md +79 -79
  16. package/examples/permissions/SailCalldata.sol +118 -118
  17. package/examples/permissions/foundry.toml +10 -10
  18. package/examples/permissions/interfaces/IBatchPermission.sol +38 -38
  19. package/examples/permissions/interfaces/IPermission.sol +18 -18
  20. package/package.json +44 -45
  21. package/packages/cli/README.md +34 -34
  22. package/packages/cli/dist/index.cjs +734 -705
  23. package/packages/cli/dist/server.cjs +627 -538
  24. package/packages/sdk/README.md +65 -65
  25. package/packages/sdk/dist/intelligence.d.ts +1 -1
  26. package/packages/sdk/dist/intelligence.js +1 -1
  27. package/packages/sdk/package.json +80 -80
  28. package/packages/ui/dist/assets/{add-BxpXfVWe.js → add-Dl1etsL9.js} +1 -1
  29. package/packages/ui/dist/assets/{all-wallets-BKTn_sWK.js → all-wallets-C0eHLOGG.js} +1 -1
  30. package/packages/ui/dist/assets/{app-store-CfuKbwxR.js → app-store-B-VMDEZ3.js} +1 -1
  31. package/packages/ui/dist/assets/{apple-BKSBbNYg.js → apple-DkDXzKns.js} +1 -1
  32. package/packages/ui/dist/assets/{arrow-bottom-D4bG6gZi.js → arrow-bottom-DtPzuS76.js} +1 -1
  33. package/packages/ui/dist/assets/{arrow-bottom-circle-BNTs1p0T.js → arrow-bottom-circle-D7odSAO8.js} +1 -1
  34. package/packages/ui/dist/assets/{arrow-left-2uee3vYv.js → arrow-left-zJV9tpx0.js} +1 -1
  35. package/packages/ui/dist/assets/{arrow-right-BktjMV6h.js → arrow-right-BOREfe7o.js} +1 -1
  36. package/packages/ui/dist/assets/{arrow-top-Izu28fX4.js → arrow-top-CipQc3Af.js} +1 -1
  37. package/packages/ui/dist/assets/{bank-USBaAyFM.js → bank-C5s7eoV5.js} +1 -1
  38. package/packages/ui/dist/assets/{basic-C_9KjTEH.js → basic-D2es4Vq8.js} +1 -1
  39. package/packages/ui/dist/assets/{browser-DAEMAKV7.js → browser-DITQWDC9.js} +1 -1
  40. package/packages/ui/dist/assets/{card-DT8yDkKN.js → card-C3DDkaYK.js} +1 -1
  41. package/packages/ui/dist/assets/{ccip-CkqfGSxX.js → ccip-UBXL3JiN.js} +1 -1
  42. package/packages/ui/dist/assets/{checkmark-bold-D2gjOQo2.js → checkmark-bold-D8yW0_K_.js} +1 -1
  43. package/packages/ui/dist/assets/{checkmark-CsgdEXFj.js → checkmark-ngef3MAl.js} +1 -1
  44. package/packages/ui/dist/assets/{chevron-bottom-tprFynYV.js → chevron-bottom-C56BipDR.js} +1 -1
  45. package/packages/ui/dist/assets/{chevron-left-D2Zj1gNB.js → chevron-left-BmIPtPl_.js} +1 -1
  46. package/packages/ui/dist/assets/{chevron-right-D1rRuAVe.js → chevron-right-BnySHQ8h.js} +1 -1
  47. package/packages/ui/dist/assets/{chevron-top-24dL1mbL.js → chevron-top-BDGZnNW3.js} +1 -1
  48. package/packages/ui/dist/assets/{chrome-store-Vy-5niYX.js → chrome-store-BYIqJZVF.js} +1 -1
  49. package/packages/ui/dist/assets/{clock-qBjLnVdJ.js → clock-Bl4mUHAM.js} +1 -1
  50. package/packages/ui/dist/assets/{close-DARDwgcu.js → close-B9rhEX6U.js} +1 -1
  51. package/packages/ui/dist/assets/{coinPlaceholder-BvpIbPlD.js → coinPlaceholder-1cO0FQsl.js} +1 -1
  52. package/packages/ui/dist/assets/{compass-BMTO0ayt.js → compass-7i-VuXu2.js} +1 -1
  53. package/packages/ui/dist/assets/{copy-PaXeRHza.js → copy-OqqXix2J.js} +1 -1
  54. package/packages/ui/dist/assets/{core-BFnStQd-.js → core-tX9kIIDJ.js} +3 -3
  55. package/packages/ui/dist/assets/cursor-BoyeQ9fN.js +3 -0
  56. package/packages/ui/dist/assets/{cursor-transparent-BEMdi-8q.js → cursor-transparent-5aoRH67u.js} +1 -1
  57. package/packages/ui/dist/assets/{desktop-CfuLLThw.js → desktop-BaPXK9R6.js} +1 -1
  58. package/packages/ui/dist/assets/{disconnect-DhwgJMiR.js → disconnect-LlK5K1CF.js} +1 -1
  59. package/packages/ui/dist/assets/{discord-po8qoN1s.js → discord-BdcQNWY_.js} +1 -1
  60. package/packages/ui/dist/assets/{etherscan-BEsz0_yx.js → etherscan-Bb-WxpO1.js} +1 -1
  61. package/packages/ui/dist/assets/{events-Bz33Unzu.js → events-DjdZr6no.js} +1 -1
  62. package/packages/ui/dist/assets/{exclamation-triangle-7CjTAGOQ.js → exclamation-triangle-COx4VtPV.js} +1 -1
  63. package/packages/ui/dist/assets/{extension-CmxjEWEt.js → extension-DF63DTWO.js} +1 -1
  64. package/packages/ui/dist/assets/{external-link-CmQ--bNS.js → external-link-DyghCkQu.js} +1 -1
  65. package/packages/ui/dist/assets/{facebook-CIBn9b65.js → facebook-Dcg4bZMR.js} +1 -1
  66. package/packages/ui/dist/assets/{fallback-DATyrQlb.js → fallback-DJIr_fH3.js} +1 -1
  67. package/packages/ui/dist/assets/{farcaster-OJ3Jasxg.js → farcaster-BkmV5HjO.js} +1 -1
  68. package/packages/ui/dist/assets/{filters-D4x09zeL.js → filters-DLHj1T_P.js} +1 -1
  69. package/packages/ui/dist/assets/{github-ZlIuMArp.js → github-BFDCgKrF.js} +1 -1
  70. package/packages/ui/dist/assets/{google-Gwg85sfv.js → google-C08SpmIy.js} +1 -1
  71. package/packages/ui/dist/assets/{help-circle-D1uOWYcX.js → help-circle-DU1IFmWp.js} +1 -1
  72. package/packages/ui/dist/assets/{id-C0-5UdYk.js → id-DtDRGf3L.js} +1 -1
  73. package/packages/ui/dist/assets/{image-D_DUsv8-.js → image-CgCXJEjT.js} +1 -1
  74. package/packages/ui/dist/assets/{index-izd7vu_r.js → index-8chM4S5Y.js} +1 -1
  75. package/packages/ui/dist/assets/{index-CrYzBWfD.js → index-B5sCtNuq.js} +1 -1
  76. package/packages/ui/dist/assets/{index-DdbJhIdl.js → index-BBfBEazf.js} +3 -3
  77. package/packages/ui/dist/assets/{index-BCzex_R6.js → index-BhXPwltt.js} +1 -1
  78. package/packages/ui/dist/assets/index-Cm05Py20.css +1 -0
  79. package/packages/ui/dist/assets/{index-DiojfeVM.js → index-D37bD6Yt.js} +1 -1
  80. package/packages/ui/dist/assets/index-DZfBh-cg.js +1775 -0
  81. package/packages/ui/dist/assets/{index.es-DdkHhQAj.js → index.es-BEcNQEn-.js} +4 -4
  82. package/packages/ui/dist/assets/{info-CiRd_kEG.js → info-ClsdYA4P.js} +1 -1
  83. package/packages/ui/dist/assets/{info-circle-ypxjqarK.js → info-circle-DJmn4Bsv.js} +1 -1
  84. package/packages/ui/dist/assets/{lightbulb-B-pxLxd8.js → lightbulb-CXSftjXS.js} +1 -1
  85. package/packages/ui/dist/assets/{mail-BYmicuVZ.js → mail-cdYKOl9P.js} +1 -1
  86. package/packages/ui/dist/assets/{metamask-sdk-Ccl6DG7Q.js → metamask-sdk-Co3aIEln.js} +1 -1
  87. package/packages/ui/dist/assets/{mobile-CtP5PqVT.js → mobile-DEHYlk8L.js} +1 -1
  88. package/packages/ui/dist/assets/{more-6C2733we.js → more-Cq_fo8pI.js} +1 -1
  89. package/packages/ui/dist/assets/{network-placeholder-CdhxMzqd.js → network-placeholder-_dLCK4xB.js} +1 -1
  90. package/packages/ui/dist/assets/{nftPlaceholder-DVmTWEAY.js → nftPlaceholder-Dz4HKEr4.js} +1 -1
  91. package/packages/ui/dist/assets/{off-DNYLughs.js → off-B1k1lhkr.js} +1 -1
  92. package/packages/ui/dist/assets/{parseSignature-Dq2B5Bu3.js → parseSignature-Cr0ptV2X.js} +1 -1
  93. package/packages/ui/dist/assets/{play-store-D7Qut5ta.js → play-store-lYqe4eeL.js} +1 -1
  94. package/packages/ui/dist/assets/{plus-kqMyjt3q.js → plus-QMgh1krr.js} +1 -1
  95. package/packages/ui/dist/assets/{qr-code-DiUCWRbz.js → qr-code-ClVHbZWN.js} +1 -1
  96. package/packages/ui/dist/assets/{recycle-horizontal-Boe3XiS-.js → recycle-horizontal-CxGYnWid.js} +1 -1
  97. package/packages/ui/dist/assets/{refresh-CrBgBQYO.js → refresh-CPysMza_.js} +1 -1
  98. package/packages/ui/dist/assets/{reown-logo-CFZCCHSx.js → reown-logo-OoL_zJd0.js} +1 -1
  99. package/packages/ui/dist/assets/{search-ChTDrghU.js → search-2GaRbf1I.js} +1 -1
  100. package/packages/ui/dist/assets/{secp256k1-DAV5Q_FR.js → secp256k1-BrB8qSSy.js} +1 -1
  101. package/packages/ui/dist/assets/{send-DLFbBFe1.js → send-CC2UuIfD.js} +1 -1
  102. package/packages/ui/dist/assets/{swapHorizontal-BEs3emfG.js → swapHorizontal-BRqYwsqT.js} +1 -1
  103. package/packages/ui/dist/assets/{swapHorizontalBold-CC-Hfa7W.js → swapHorizontalBold-Bj0GSRq9.js} +1 -1
  104. package/packages/ui/dist/assets/{swapHorizontalMedium-BmR0H8DC.js → swapHorizontalMedium-oLOjpU2A.js} +1 -1
  105. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BdP5NGIH.js → swapHorizontalRoundedBold-TJ652QXb.js} +1 -1
  106. package/packages/ui/dist/assets/{swapVertical-CPrGEJPY.js → swapVertical-e0NLyV3x.js} +1 -1
  107. package/packages/ui/dist/assets/{telegram-CxNoZ80Q.js → telegram-WhJHVeoU.js} +1 -1
  108. package/packages/ui/dist/assets/{three-dots-BRa6SBpL.js → three-dots-BlBAOyW-.js} +1 -1
  109. package/packages/ui/dist/assets/{twitch-BC338bG5.js → twitch-BH7vWmPc.js} +1 -1
  110. package/packages/ui/dist/assets/{twitterIcon-BGZmt2i9.js → twitterIcon-As0Nkanp.js} +1 -1
  111. package/packages/ui/dist/assets/{verify-CEstW0zw.js → verify-BEJ0QuLl.js} +1 -1
  112. package/packages/ui/dist/assets/{verify-filled-OkZb0weU.js → verify-filled-B8Ww2N7z.js} +1 -1
  113. package/packages/ui/dist/assets/{w3m-modal-pS09ECwE.js → w3m-modal-5rOSZgOR.js} +1 -1
  114. package/packages/ui/dist/assets/{wallet-BXVKCgC9.js → wallet-CAfC3aml.js} +1 -1
  115. package/packages/ui/dist/assets/{wallet-placeholder-C_kNhB1c.js → wallet-placeholder-4RZI464Z.js} +1 -1
  116. package/packages/ui/dist/assets/{walletconnect-CRKIuUHH.js → walletconnect-BiltKqAe.js} +1 -1
  117. package/packages/ui/dist/assets/{warning-circle-DB2NnwlJ.js → warning-circle-CI4jqpHo.js} +1 -1
  118. package/packages/ui/dist/assets/{x-DT4RmwL5.js → x-FBttjBWO.js} +1 -1
  119. package/packages/ui/dist/index.html +14 -14
  120. package/scripts/check-docs.mjs +262 -262
  121. package/scripts/check-init.mjs +108 -108
  122. package/templates/custom-mandate/.sail/contracts/interfaces/IPermission.sol +18 -18
  123. package/templates/custom-mandate/README.md +116 -116
  124. package/templates/custom-mandate/foundry.toml +8 -8
  125. package/templates/custom-mandate/mandates/BoundedCallPermission.sol +41 -41
  126. package/templates/custom-mandate/mandates/README.md +16 -16
  127. package/templates/custom-mandate/mandates/SailCalldata.sol +118 -118
  128. package/templates/default/.cursor/rules +25 -25
  129. package/templates/default/.env.example +20 -20
  130. package/templates/default/.github/workflows/agent-tick.yml +33 -33
  131. package/templates/default/.sail/README.md +13 -13
  132. package/templates/default/.sail/config.json +10 -10
  133. package/templates/default/AGENTS.md +171 -171
  134. package/templates/default/CLAUDE.md +2 -2
  135. package/templates/default/README.md +16 -16
  136. package/templates/default/_gitignore +13 -13
  137. package/templates/default/docs/PERMISSION_MODEL.md +93 -93
  138. package/templates/default/examples/dca/README.md +16 -16
  139. package/templates/default/examples/dca/agent.ts +174 -174
  140. package/templates/default/examples/dca/mandate.ts +45 -45
  141. package/templates/default/package.json +17 -17
  142. package/templates/default/src/agent.ts +37 -37
  143. package/templates/default/src/config.ts +24 -24
  144. package/templates/default/src/mandate.ts +22 -22
  145. package/templates/default/tsconfig.json +17 -17
  146. package/templates/default/ui/README.md +3 -3
  147. package/templates/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +84 -84
  148. package/templates/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +97 -97
  149. package/templates/lifi-permissions/README.md +53 -53
  150. package/packages/ui/dist/assets/cursor-BDvw-B17.js +0 -3
  151. package/packages/ui/dist/assets/index-BUhrHLpY.js +0 -1775
  152. package/packages/ui/dist/assets/index-Cq02kQmy.css +0 -1
  153. package/scripts/postinstall.js +0 -81
package/README.md CHANGED
@@ -1,430 +1,428 @@
1
- # Sailor
2
-
3
- > A toolkit for building and operating Sail Protocol SMAs with AI agents.
4
-
5
- Sailor is the operator layer for [Sail Protocol](../SailProtocol): the tooling an agent builder uses to create a Separately Managed Account, bound it with permissions, and run a strategy against it. It wraps the on-chain primitives — SailKernel dispatch, MandateFactory registration, EIP-712 mandate signing behind a TypeScript SDK, a CLI, and a local dashboard. An agent is an async function that receives context and returns intended transactions; Sailor previews each through the kernel, executes the approved ones, and records what happened. It does not deploy the protocol or author new permission templates — that lives in Sail Protocol. It sits one level up: turning a deployed SailKernel into something an operator can actually drive.
6
-
7
- ---
8
-
9
- ## What's inside
10
-
11
- | Package | Name | Role |
12
- |---|---|---|
13
- | `packages/sdk` | `@sail/sdk` (internal) | TypeScript library: SailorClient, EIP-712 helpers, ABIs, chain registry |
14
- | `packages/cli` | `@sail.money/sailor` | CLI for account setup, mandate signing, and agent execution |
15
- | `packages/ui` | `sailor-ui` | Local dashboard running on localhost:3333 |
16
- | `templates/default` | — | Default agent starter (neutral; what `sailor init` scaffolds) |
17
- | `templates/custom-mandate` | — | Solidity reference: IPermission scaffold (not a project template) |
18
- | `templates/lifi-permissions` | — | Solidity reference: LiFi clone permission contracts (not a project template) |
19
-
20
- ---
21
-
22
- ## How it works
23
-
24
- The path from nothing to a running agent is five stages, guided by your AI coding assistant through the scaffolded `AGENTS.md`:
25
-
26
- 1. **Deploy your SMA and create your agent wallet** — done in the browser. Your owner wallet never leaves it.
27
- 2. **Define your strategy** — describe what you want your agent to do. The assistant asks the right questions to establish on-chain bounds (tokens, amounts, venues), then helps design the permission contracts.
28
- 3. **Build, test, and sign your mandate** — the assistant authors the permission contracts, proves in plain English what each one permits and blocks, deploys them, and walks you through signing to authorize.
29
- 4. **Run** `sailor run` executes your agent locally on a schedule, or via the GitHub Actions workflow the scaffold provides.
30
- 5. **Extend** *(optional)* the assistant can wire notifications (Telegram, email) and build a custom dashboard tailored to your strategy.
31
-
32
- Run `npx sailor init my-agent`, open the scaffolded folder in Claude Code, Cursor, Codex, or any AI coding assistant, and say **"start"**. The `AGENTS.md` in the project drives the assistant through all five stages.
33
-
34
- ---
35
-
36
- ## Roles
37
-
38
- Sailor operates the three roles Sail Protocol separates:
39
-
40
- | Role | Authority | Held by |
41
- |---|---|---|
42
- | **Owner** | Holds the Safe. Custody anchor. | The LP (Safe owner) — same wallet as MetaMask |
43
- | **Permission Signer** | Signs mandate registration and revocation via EIP-712. | Same as Owner, or a separate key |
44
- | **Manager** | Executes dispatches within permitted bounds. Signs each dispatch. | The agent key — encrypted in `.sail/keys/manager.json` |
45
-
46
- ---
47
-
48
- ## Installation
49
-
50
- ### Start a new agent project (recommended)
51
-
52
- Open your AI coding assistant and run in its terminal:
53
-
54
- ```sh
55
- npx sailor init my-agent
56
- ```
57
-
58
- Then say **"start"** your assistant takes it from there.
59
-
60
- ### Global CLI (for direct sailor commands)
61
-
62
- ```sh
63
- npm install -g @sail.money/sailor
64
- sailor init my-agent
65
- ```
66
-
67
- ---
68
-
69
- ## Quickstart
70
-
71
- Prerequisites:
72
-
73
- - Node.js 18+
74
- - A wallet (MetaMask or Rabby)
75
- - An RPC URL (e.g. Alchemy free tier)
76
- - A supported chain: **Base, Base Sepolia, Arbitrum, or Unichain** — verified deployments are bundled in `@sail.money/sailor`.
77
-
78
- ### Recommended assistant-driven
79
-
80
- ```bash
81
- npx sailor init my-agent && cd my-agent
82
- npm install
83
- ```
84
-
85
- Open this folder in Claude Code, Cursor, Codex, or any AI coding assistant and say **"start"**. The scaffolded `AGENTS.md` guides the assistant through all five stages — SMA deployment, strategy definition, mandate authoring, running, and automation. No manual steps required.
86
-
87
- ### Direct CLI reference (advanced)
88
-
89
- For users who prefer the terminal:
90
-
91
- ```bash
92
- sailor capabilities # what you can build on this chain — read-only, no gas
93
- sailor doctor # kernel model + RPC reachability + gas balances
94
- sailor ui start # open http://localhost:3333 to deploy SMA + create agent wallet
95
- sailor run --once # single tick — confirm it works before automating
96
- sailor run # start the agent (continuous)
97
- sailor keys export-ci # copy the encrypted agent wallet to ci-keystore.json for CI commits
98
- sailor mandate sign # sign a mandate — reconciles against live on-chain permissions first
99
- ```
100
-
101
- `sailor run` writes reverted transactions to stderr as `reverted: <txHash> (gas used: N)`; successful dispatches are appended to `.sail/activity.jsonl`.
102
-
103
- ---
104
-
105
- ## Templates
106
-
107
- `sailor init` scaffolds a new agent project from a template. By default it
108
- writes into the **current directory**; pass a name to create a subdirectory.
109
-
110
- ```bash
111
- sailor init # scaffold into cwd
112
- sailor init my-agent # create ./my-agent/ and scaffold there
113
- sailor init --template default # explicit (same as default)
114
- sailor init my-agent --template <name> # named subdirectory + specific template
115
- ```
116
-
117
- ### Available templates
118
-
119
- | Template | Description |
120
- |---|---|
121
- | `default` | Neutral agent starter. Includes a blank agent loop, Foundry workspace for permission contracts, GitHub Actions cron job, and the operator guide (`AGENTS.md`). For a complete worked example see `examples/dca/`. **Default.** |
122
-
123
- ### What makes a valid template
124
-
125
- A valid template is any directory under `templates/` that contains a
126
- `package.json`. Directories without one (e.g. `custom-mandate`,
127
- `lifi-permissions`) are Solidity reference sources, not project scaffolds, and
128
- are excluded from the available list.
129
-
130
- ### Adding a template
131
-
132
- 1. Create a directory under `templates/<your-template-name>/`.
133
- 2. Add a `package.json` (the `name` field is patched to the project name on
134
- init).
135
- 3. Add a `.sail/` workspace structure if the agent needs local state.
136
- 4. The template will appear automatically in `sailor init --template <name>`.
137
-
138
- Template files are bundled into the published `sailor` npm package via the
139
- `files` field in the root `package.json`.
140
-
141
- ---
142
-
143
- ## Dashboard (`sailor ui`)
144
-
145
- The Sailor dashboard is a local React app served at `http://localhost:3333`.
146
- It shows live account state, mandate health, signer balances, and recent
147
- activity — all read from the project's `.sail/` directory with no hosted
148
- backend.
149
-
150
- ### Commands
151
-
152
- ```bash
153
- sailor ui # start the dashboard (same as sailor ui start)
154
- sailor ui start # start the dashboard at http://localhost:3333
155
- sailor ui stop # stop the running dashboard
156
- sailor ui status # show whether the dashboard is running + pid
157
- ```
158
-
159
- ### How it works
160
-
161
- `sailor ui start` spawns a bundled Express server (`server.cjs`) that:
162
-
163
- - Serves the pre-built React UI as static files on `/`
164
- - Exposes a local API on `/api` that reads `.sail/` state from the current
165
- working directory
166
-
167
- The server PID is written to `.sail/runtime/ui.json` on start. `sailor ui stop`
168
- reads that file, sends `SIGTERM` to the server process, and removes the file.
169
- This means you can start the dashboard in one terminal and stop it from another.
170
-
171
- ### Running in the background
172
-
173
- ```bash
174
- # macOS / Linux
175
- sailor ui start &
176
- sailor ui status # running http://localhost:3333 (pid 12345)
177
- sailor ui stop # Stopped Sailor UI (pid 12345).
178
-
179
- # Windows (PowerShell)
180
- Start-Job { sailor ui start }
181
- sailor ui status
182
- sailor ui stop
183
- ```
184
-
185
- ---
186
-
187
- ## Agent-driven onboarding & custom mandates
188
-
189
- For chains with a bundled Sail deployment (Base, Base Sepolia, Arbitrum, Unichain), an agent can drive the whole
190
- setup through a browser **signing station**. The station is a local HTTP +
191
- WebSocket daemon that bridges the CLI and the owner's wallet: the agent never
192
- holds the owner key — it pushes signing requests, the owner approves them in the
193
- browser, and the agent submits the transactions it's allowed to.
194
-
195
- ```bash
196
- sailor keys generate # manager (agent) key
197
- sailor station start & # signing daemon (serves the UI)
198
- # owner opens the printed URL once and connects their wallet
199
- sailor owner connect # detect & persist the owner
200
- sailor scan # discover the owner's Safes + state
201
- sailor onboard --new-sma # create an SMA + (optionally) attach a mandate
202
- ```
203
-
204
- Agents author their own permission contracts and deploy them from the scaffolded
205
- Foundry workspace (`mandates/`, with `@sail/interfaces/IPermission.sol` vendored
206
- under `.sail/contracts/`):
207
-
208
- ```bash
209
- forge build
210
- sailor mandate deploy --contract MyMandate \
211
- --args '["0xPermissionSigner", ["0xTarget"]]' \
212
- --attach --sma 0xSafe
213
- ```
214
-
215
- `deploy` emits a contract-creation signing request (the owner signs it in the
216
- browser); the deployed address is read from the receipt and tracked in
217
- `.sail/state/mandates.json`. `attach` reads the signer nonce, has the owner sign
218
- a `RegisterPermission` EIP-712 message, then the agent submits
219
- `kernel.registerPermission` with the exact registration fee. Every command takes
220
- `--json` for headless agent use; set `SAIL_PASSPHRASE` to unlock the manager key
221
- non-interactively.
222
-
223
- `sailor mandate sign` reconciles against the live on-chain `getPermissions()` call
224
- before building the mandate payload — permissions revoked on-chain are excluded even
225
- if they remain in the local `.sail/state/mandates.json` (which is an append-only
226
- historical record and is never modified by the reconciliation).
227
-
228
- ### GitHub Actions CI
229
-
230
- The scaffolded `.github/workflows/agent-tick.yml` runs `sailor run --once` on a
231
- cron schedule using `npm ci` (no pnpm required). Setup:
232
-
233
- 1. `sailor keys export-ci` — copies the encrypted agent wallet to `ci-keystore.json`
234
- in the project root and allowlists it in `.gitignore`. The geth v3 keystore is
235
- safe to commit; the raw private key is never exposed.
236
- 2. Commit `ci-keystore.json`, `.sail/account.json`, and `.sail/mandate.json`.
237
- 3. Add two repository secrets (Settings → Secrets → Actions):
238
- - `SAIL_PASSPHRASE` the passphrase that encrypts the agent wallet
239
- - `RPC_URL` — your RPC endpoint
240
-
241
- The workflow copies `ci-keystore.json` to `.sail/keys/manager.json`, then calls
242
- `npx sailor run --once` with `SAIL_PASSPHRASE` set so the key is unlocked
243
- non-interactively. No private key ever appears in the workflow file or in secrets.
244
-
245
- ---
246
-
247
- ## Packages
248
-
249
- Sailor ships as a **single npm package** — the SDK is bundled inside it and exposed via a subpath export:
250
-
251
- | Package | Contents |
252
- |---|---|
253
- | `@sail.money/sailor` | CLI binary, UI server, templates, examples, and SDK |
254
-
255
- The SDK is available as a subpath export for use in agent code:
256
-
257
- ```ts
258
- import type { Agent, AgentContext, Dispatch } from '@sail.money/sailor/sdk'
259
- ```
260
-
261
- ### npm (`publish-npm.yml`)
262
-
263
- Published to the public npm registry under the `@sail.money` scope.
264
-
265
- | Trigger | Package | Version | dist-tag |
266
- |---|---|---|---|
267
- | Tag push (`v*`) | `@sail.money/sailor` | `1.0.0` | `latest` |
268
- | Manual dispatch | `@dev.sail.money/sailor` | `1.0.0-42` | `dev` |
269
-
270
- ```bash
271
- npm install @sail.money/sailor # latest stable (tag push)
272
- ```
273
-
274
- For dev builds, the package name changes scope to `@dev.sail.money`. Use an alias so your import paths stay the same:
275
-
276
- ```bash
277
- npm install "@sail.money/sailor@npm:@dev.sail.money/sailor@dev"
278
- ```
279
-
280
- This installs the latest dev build and makes it available as `@sail.money/sailor` locally — `@sail.money/sailor/sdk` imports continue to work unchanged.
281
-
282
- ### GitHub Packages (`publish.yml`)
283
-
284
- Published to GitHub Packages under the `@sail-money` scope for internal testing no public npm registry required.
285
-
286
- | Trigger | Package | dist-tag |
287
- |---|---|---|
288
- | Merge to `main` | `@sail-money/sailor` | `latest` |
289
- | Manual dispatch | `@sail-money/sailor-dev` | `dev` |
290
-
291
- Both builds require an alias since the package scope differs from `@sail.money`:
292
-
293
- ```bash
294
- npm install "@sail.money/sailor@npm:@sail-money/sailor@latest" --registry https://npm.pkg.github.com
295
- npm install "@sail.money/sailor@npm:@sail-money/sailor-dev@dev" --registry https://npm.pkg.github.com
296
- ```
297
-
298
- Or pin in `package.json`:
299
-
300
- ```json
301
- "dependencies": {
302
- "@sail.money/sailor": "npm:@sail-money/sailor@latest"
303
- }
304
- ```
305
-
306
- ```json
307
- "dependencies": {
308
- "@sail.money/sailor": "npm:@sail-money/sailor-dev@dev"
309
- }
310
- ```
311
-
312
- Either way, `@sail.money/sailor/sdk` imports work unchanged.
313
-
314
- ---
315
-
316
- ## Architecture
317
-
318
- ```
319
- ┌────────────────────┐ ┌────────────────────┐
320
- │ Permission Signer │ │ Manager/Agent │
321
- │ MetaMask / local │ │ .sail/keys/manager
322
- └─────────┬──────────┘ └─────────┬──────────┘
323
- │ │
324
- EIP-712 mandate │ dispatch
325
- ▼ ▼
326
- ┌─────────────────────────────────────────────────────────────────────┐
327
- │ SailKernel │
328
- │ (Sail Protocol) │
329
- └─────────┬───────────────────────┬───────────────────────┬───────────┘
330
- │ │ │
331
- │ registration │ execution │ evaluation
332
- ▼ ▼ ▼
333
- ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
334
- │ MandateFactory │ │ Safe │ │ Permissions │
335
- │ (register perms) │ │ (custody) │ │ (named, per-call) │
336
- └────────────────────┘ └────────────────────┘ └────────────────────┘
337
-
338
- sailor CLI / @sail.money/sailor/sdk drive both signing paths above.
339
- .sail/ (account · mandate · activity) ──→ sailor-ui (localhost:3333)
340
- ```
341
-
342
- The CLI and SDK sit between the operator and SailKernel: they build the EIP-712 payloads, submit dispatches, and read kernel state via viem. The permission signer authorizes the mandate — registration runs through MandateFactory — while the manager key signs each dispatch the kernel evaluates against a named permission before executing it through the Safe. All local state — the deployed account, the signed mandate, and the agent's activity log — lives under `.sail/` on disk, which the dashboard reads through a small local server. Sailor never holds the Owner key and runs no hosted backend; the wallet talks to the chain directly.
343
-
344
- ---
345
-
346
- ## Security model
347
-
348
- - The agent signs dispatches; the kernel evaluates the named permission on every call. A permission returning false or exceeding its gas cap is treated as denial — fail-closed.
349
- - The Owner key controls the Safe and is never read by Sailor. Mandate signing requires a deliberate action by the permission signer.
350
- - The manager key is encrypted on disk using geth keystore v3 (scrypt + aes-128-ctr) and is never transmitted.
351
- - The session can be paused instantly via `sailor session pause` or the dashboard stop button; this does not affect Safe custody.
352
- - All addresses passed to the CLI are normalized with `getAddress()` (EIP-55 checksum). Mixed-case or lowercase inputs are accepted and canonicalized before any on-chain call or state write.
353
-
354
- ---
355
-
356
- ## State of the project
357
-
358
- Sailor is functional and published as [`@sail.money/sailor`](https://www.npmjs.com/package/@sail.money/sailor) on npm (v0.0.1). The SDK, CLI, keystore, mandate flows, agent runner, and dashboard are implemented and have been exercised end to end against Base Sepolia.
359
-
360
- The Sail Protocol trusted core is deployed on Base, Base Sepolia, Arbitrum, and Unichain as staging deployments for testing ahead of a formal launch. All four run the selective dispatch model, with verified deployments bundled in `@sail.money/sailor`. These deployments are under an ongoing external audit by [Octane Security](https://octane.security) and are not final — do not use them with funds you are not prepared to lose. Permission templates are not yet deployed against the Base, Arbitrum, and Base Sepolia kernels; **Unichain** ships the full template suite (7 shared + 12 standalone, source-verified) and its template registries are populated. The remaining template registries will be filled in as templates are deployed on the other chains and at mainnet launch.
361
-
362
- ---
363
-
364
- ## Deployments
365
-
366
- The Sail Protocol trusted core is live on the following chains as **staging deployments** ahead of a formal launch. All addresses are bundled in `@sail.money/sailor` and run the selective dispatch model with zero fees. Permission templates are not yet deployed against the Base, Arbitrum, and Base Sepolia kernels; **Unichain** ships the full template suite (7 shared + 12 standalone, source-verified on uniscan.xyz) and has its onboarding allowlists seeded at genesis.
367
-
368
- ### Base (8453)
369
-
370
- | Contract | Address |
371
- |---|---|
372
- | SailKernel | `0x6319d3dfDDe3804ba93D65752b00c52bFb05a1ab` |
373
- | SailGovernance | `0x7E897D919872b1587577617ffFC42113679d0C50` |
374
- | Timelock | `0x8eC3Ca951E193C6E3713A70022454d7A1f083281` |
375
- | PermissionFactory | `0x7724EACd97C8601d5AC244Aadbf76ad87353Ff31` |
376
- | StandardFeePolicy | `0x65850a8D5050aeAade68289ff96c4F119a24B82e` |
377
- | SafeModuleEnabler | `0xC84EdE78f93291A1fab19F51c4c7e938AB302Edf` |
378
- | Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
379
-
380
- ### Arbitrum (42161)
381
-
382
- | Contract | Address |
383
- |---|---|
384
- | SailKernel | `0x2716B12832DED0EF5688519c5Fe069EFc0374E02` |
385
- | SailGovernance | `0xd6AbB7A1036ADc7958Abffec9Da03450c5a2Ec8e` |
386
- | Timelock | `0x114CB7110C780f7E3a6093AfE0B52463a569857C` |
387
- | PermissionFactory | `0x23681A8A4C9819D8EaB37E46B858da6F3c85E683` |
388
- | StandardFeePolicy | `0xAdfB986D48480bC67a7cF3751d30599161632e0D` |
389
- | SafeModuleEnabler | `0xabe2a6D03F592BC602cA1dBDCD885ba2493274f9` |
390
- | Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
391
-
392
- ### Base Sepolia (84532)
393
-
394
- | Contract | Address |
395
- |---|---|
396
- | SailKernel | `0xf1D0F4C9893612627409948BAa9d82a01a373799` |
397
- | SailGovernance | `0xEaD44bC6999E7b00b9b2E11c1660248DC2a30993` |
398
- | Timelock | `0x97B863e392C9859336788D5Ec454527d33C95B74` |
399
- | PermissionFactory | `0xdfF6a2272F667cDf78Af4681b9c88A219998db95` |
400
- | StandardFeePolicy | `0x05570F7973b46Eb9Ed4518422891EFC26BD58b97` |
401
- | SafeModuleEnabler | `0xB2C2B52d94412e3472C9fb2B52186eA12a935869` |
402
- | Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
403
-
404
- ### Unichain (130)
405
-
406
- First chain to ship the full permission-template suite (7 shared + 12 standalone, source-verified on [uniscan.xyz](https://uniscan.xyz)). Genesis allowlist bootstrap — onboarding usable without the 48h timelock.
407
-
408
- | Contract | Address |
409
- |---|---|
410
- | SailKernel | `0xD985029960a9B7C2E7E38e102C448b8b8539B156` |
411
- | SailGovernance | `0xAb5C90ECfF2763f6f20f8E553E3b8778dD9C349A` |
412
- | Timelock | `0xd44FbBB37f01e235E0EE5386948F216d36D0CEf2` |
413
- | PermissionFactory | `0x8edDb62Aa49CeB837abf2653be2d93Ad9Fe6777D` |
414
- | StandardFeePolicy | `0x7bBA8BE3c01c972757aA4a230A00D58aB600A1F1` |
415
- | SafeModuleEnabler | `0xFE9227A9F2baf704060c604466df354a5A137b9B` |
416
- | Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
417
-
418
- The 19 template addresses are in `packages/sdk/src/deployments.ts` (`knownTemplates` + `standaloneTemplates` for chain 130).
419
-
420
- ---
421
-
422
- ## Contributing
423
-
424
- Sailor and Sail Protocol are separate repositories with separate concerns. Protocol questions — SailKernel internals, permission templates, MandateFactory, fee policies — belong in the [SailProtocol](../SailProtocol) repository. Sailor questions — the SDK, CLI, dashboard, and agent templates — belong here.
425
-
426
- ---
427
-
428
- ## License
429
-
430
- MIT
1
+ # Sailor
2
+
3
+ > The operator toolkit for Sail Protocol — SDK, CLI, and local dashboard for building and running mandated agents.
4
+
5
+ Sailor is the off-chain operator layer for [Sail Protocol](https://github.com/sail-money/SailProtocol): the tooling an operator uses to create a Separately Managed Account, register a mandate, and run a strategy agent against it. It wraps SailKernel dispatch, MandateFactory registration, and EIP-712 mandate signing behind a TypeScript SDK, a CLI, and a local dashboard. It does not deploy the protocol or author permission templates — those live in Sail Protocol. It targets already-deployed SailKernel instances and gives operators the tooling to drive them.
6
+
7
+ ---
8
+
9
+ ## What's inside
10
+
11
+ | Package | Name | Role |
12
+ |---|---|---|
13
+ | `packages/sdk` | `@sail.money/sdk` / `@sail.money/sailor/sdk` | TypeScript library: SailorClient, EIP-712 helpers, ABIs, deployment registry, chain registry |
14
+ | `packages/cli` | `@sail.money/sailor` | CLI for account setup, mandate signing, and agent execution |
15
+ | `packages/ui` | `sailor-ui` | Local dashboard running at localhost:3333 |
16
+ | `templates/default` | — | Default agent starter (neutral; what `sailor init` scaffolds) |
17
+ | `templates/custom-mandate` | — | Solidity reference: IPermission scaffold (not a project template) |
18
+ | `templates/lifi-permissions` | — | Solidity reference: LiFi clone permission contracts (not a project template) |
19
+
20
+ ---
21
+
22
+ ## Protocol model
23
+
24
+ ```mermaid
25
+ flowchart TD
26
+ Owner["**Owner**<br/>holds the Safe · signs the mandate"]
27
+ Manager["**Manager**<br/>agent · signs dispatches"]
28
+ SMA["**SMA**<br/>Safe · holds assets · executes"]
29
+ Mandate["**Mandate**<br/>set of permission contracts"]
30
+ Kernel["**Sail Kernel**<br/>evaluates permission · trusted core<br/>dispatches to Safe on success"]
31
+
32
+ Owner -- "01 deploys & owns" --> SMA
33
+ Owner -- "02 signs mandate (EIP-712)" --> Mandate
34
+ Owner -- "03 appoints · instant revocation" --> Manager
35
+ Manager -- "04 signs dispatch (EIP-712)" --> Kernel
36
+ Mandate -- "05 defines bounds" --> Kernel
37
+ Kernel -- "06 ✓ executes · ✗ outside mandate: reverts" --> SMA
38
+ ```
39
+
40
+ Sailor is the operator tooling that drives the Manager/dispatch and mandate-registration flows (steps 02–05).
41
+
42
+ ---
43
+
44
+ ## Roles
45
+
46
+ Sail Protocol separates three authority roles. Sailor operates all of them:
47
+
48
+ | Role | Authority | Held by |
49
+ |---|---|---|
50
+ | **Owner** | Holds the Safe. Custody anchor. Always self-custodial. | The LP (Safe owner) |
51
+ | **Permission Signer** | Authorizes the mandate. Signs registration and revocation via EIP-712. | Same as Owner, or a separate signing key |
52
+ | **Manager** | Executes dispatches within mandate bounds. Signs each dispatch. | The agent wallet — encrypted in `.sail/keys/manager.json` |
53
+
54
+ ---
55
+
56
+ ## How it works
57
+
58
+ The path from nothing to a running agent follows the protocol lifecycle:
59
+
60
+ 1. **Deploy your SMA** `sailor onboard --new-sma` creates the SMA on-chain. `sailor account predict` computes the deterministic address in advance. The same owner, permission signer, manager, and salt produce the same SMA address on every supported chain.
61
+ 2. **Author your permissions** — describe what the agent may do. Permission contracts encode the bounds: tokens, amounts, venues, call targets. Author them in the scaffolded Foundry workspace.
62
+ 3. **Simulate, deploy, and sign your mandate** — `sailor mandate simulate` probes a permission off-chain before authorizing it. `sailor mandate deploy --attach` deploys and registers it on-chain. `sailor mandate sign` builds and signs the registration payload against live on-chain state.
63
+ 4. **Run** `sailor run` executes the agent locally on a schedule, or via the GitHub Actions workflow the scaffold provides.
64
+ 5. **Operate** — `sailor doctor` checks kernel health and gas balances; `sailor chains` lists supported chains and deployment addresses; `sailor session pause` instantly revokes dispatch rights without touching Safe custody.
65
+
66
+ Run `npx sailor init my-agent`, open the scaffolded folder in Claude Code, Cursor, or any AI coding assistant, and say **"start"**. The `AGENTS.md` in the project guides the assistant through all five stages.
67
+
68
+ ---
69
+
70
+ ## Installation
71
+
72
+ ### Start a new agent project (recommended)
73
+
74
+ Create a folder, step into it, then install and init:
75
+
76
+ ```bash
77
+ # bash / zsh / macOS
78
+ mkdir my-agent && cd my-agent && npm i @sail.money/sailor && npx sailor init
79
+ ```
80
+
81
+ ```powershell
82
+ # PowerShell (Windows)
83
+ mkdir my-agent ; cd my-agent ; npm i @sail.money/sailor ; npx sailor init
84
+ ```
85
+
86
+ Then open the folder in your AI coding assistant and say **"start"**.
87
+
88
+ > **npx shortcut** — skips the explicit install; npm downloads sailor on the fly:
89
+ > ```sh
90
+ > mkdir my-agent && cd my-agent && npx sailor init
91
+ > ```
92
+
93
+ ### Global CLI (for direct sailor commands)
94
+
95
+ ```sh
96
+ npm install -g @sail.money/sailor
97
+ sailor init my-agent
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Quickstart
103
+
104
+ Prerequisites:
105
+
106
+ - Node.js 18+
107
+ - A wallet (MetaMask, Rabby, Phantom, and more)
108
+ - An RPC URL (e.g. Alchemy free tier)
109
+ - A supported chain: **Ethereum, Base, Arbitrum, Unichain, Base Sepolia, or Eth Sepolia** — verified deployments are bundled in `@sail.money/sailor`.
110
+
111
+ ### Recommended assistant-driven
112
+
113
+ ```bash
114
+ # bash / zsh / macOS
115
+ mkdir my-agent && cd my-agent && npm i @sail.money/sailor && npx sailor init && npm install
116
+ ```
117
+
118
+ ```powershell
119
+ # PowerShell (Windows)
120
+ mkdir my-agent ; cd my-agent ; npm i @sail.money/sailor ; npx sailor init ; npm install
121
+ ```
122
+
123
+ Open this folder in Claude Code, Cursor, Codex, or any AI coding assistant and say **"start"**. The scaffolded `AGENTS.md` guides the assistant through all five stages — SMA deployment, strategy definition, mandate authoring, running, and automation. No manual steps required.
124
+
125
+ ### Direct CLI reference
126
+
127
+ ```bash
128
+ # Discovery
129
+ sailor chains # list supported chains and kernel addresses
130
+ sailor capabilities # what you can build on this chain — read-only, no gas
131
+ sailor doctor # kernel model + RPC reachability + gas balances
132
+
133
+ # SMA setup
134
+ sailor account predict # compute deterministic SMA address before deploying
135
+ sailor onboard --new-sma # deploy SMA and optionally attach a mandate
136
+
137
+ # Mandate lifecycle
138
+ sailor mandate simulate # probe a permission off-chain (no gas) before registering
139
+ sailor mandate sign # sign the mandate — reconciles against live on-chain state
140
+ sailor mandate deploy # deploy a Foundry-compiled permission contract
141
+ sailor mandate attach # register an already-deployed permission on an SMA
142
+
143
+ # Agent operation
144
+ sailor run --once # single tick — confirm it works before automating
145
+ sailor run # start the agent (continuous)
146
+ sailor keys export-ci # copy encrypted agent wallet to ci-keystore.json for CI
147
+
148
+ # Dashboard
149
+ sailor ui start # open http://localhost:3333
150
+ ```
151
+
152
+ `sailor run` writes reverted transactions to stderr as `reverted: <txHash> (gas used: N)`; successful dispatches are appended to `.sail/activity.jsonl`.
153
+
154
+ ---
155
+
156
+ ## Templates
157
+
158
+ `sailor init` scaffolds a new agent project from a template. By default it
159
+ writes into the **current directory**; pass a name to create a subdirectory.
160
+
161
+ ```bash
162
+ sailor init # scaffold into cwd
163
+ sailor init my-agent # create ./my-agent/ and scaffold there
164
+ sailor init --template default # explicit (same as default)
165
+ sailor init my-agent --template <name> # named subdirectory + specific template
166
+ ```
167
+
168
+ ### Available templates
169
+
170
+ | Template | Description |
171
+ |---|---|
172
+ | `default` | Neutral agent starter. Includes a blank agent loop, Foundry workspace for permission contracts, GitHub Actions cron job, and the operator guide (`AGENTS.md`). For a complete worked example see `examples/dca/`. **Default.** |
173
+
174
+ ### What makes a valid template
175
+
176
+ A valid template is any directory under `templates/` that contains a
177
+ `package.json`. Directories without one (e.g. `custom-mandate`,
178
+ `lifi-permissions`) are Solidity reference sources, not project scaffolds, and
179
+ are excluded from the available list.
180
+
181
+ ### Adding a template
182
+
183
+ 1. Create a directory under `templates/<your-template-name>/`.
184
+ 2. Add a `package.json` (the `name` field is patched to the project name on
185
+ init).
186
+ 3. Add a `.sail/` workspace structure if the agent needs local state.
187
+ 4. The template will appear automatically in `sailor init --template <name>`.
188
+
189
+ Template files are bundled into the published `sailor` npm package via the
190
+ `files` field in the root `package.json`.
191
+
192
+ ---
193
+
194
+ ## Dashboard (`sailor ui`)
195
+
196
+ The Sailor dashboard is a local React app served at `http://localhost:3333`.
197
+ It shows live account state, mandate health, signer balances, and recent
198
+ activity all read from the project's `.sail/` directory with no hosted
199
+ backend.
200
+
201
+ ### Commands
202
+
203
+ ```bash
204
+ sailor ui # start the dashboard (same as sailor ui start)
205
+ sailor ui start # start the dashboard at http://localhost:3333
206
+ sailor ui stop # stop the running dashboard
207
+ sailor ui status # show whether the dashboard is running + pid
208
+ ```
209
+
210
+ ### How it works
211
+
212
+ `sailor ui start` spawns a bundled Express server (`server.cjs`) that:
213
+
214
+ - Serves the pre-built React UI as static files on `/`
215
+ - Exposes a local API on `/api` that reads `.sail/` state from the current
216
+ working directory
217
+
218
+ The server PID is written to `.sail/runtime/ui.json` on start. `sailor ui stop`
219
+ reads that file, sends `SIGTERM` to the server process, and removes the file.
220
+ This means you can start the dashboard in one terminal and stop it from another.
221
+
222
+ ### Running in the background
223
+
224
+ ```bash
225
+ # macOS / Linux
226
+ sailor ui start &
227
+ sailor ui status # ● running http://localhost:3333 (pid 12345)
228
+ sailor ui stop # Stopped Sailor UI (pid 12345).
229
+
230
+ # Windows (PowerShell)
231
+ Start-Job { sailor ui start }
232
+ sailor ui status
233
+ sailor ui stop
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Agent-driven onboarding & custom mandates
239
+
240
+ On any of the six supported chains, an agent can drive the whole setup through
241
+ a browser **signing station**. The station is a local HTTP + WebSocket daemon
242
+ that bridges the CLI and the owner's wallet: the agent never holds the owner
243
+ key it pushes signing requests, the owner approves them in the browser, and
244
+ the agent submits the transactions it's allowed to.
245
+
246
+ ```bash
247
+ sailor keys generate # create the manager (agent) key
248
+ sailor station start & # signing daemon (serves the UI)
249
+ # owner opens the printed URL once and connects their wallet
250
+ sailor owner connect # detect & persist the owner
251
+ sailor scan # discover the owner's Safes + state
252
+ sailor onboard --new-sma # create an SMA + (optionally) attach a mandate
253
+ ```
254
+
255
+ Agents author their own permission contracts and deploy them from the scaffolded
256
+ Foundry workspace (`mandates/`, with `@sail/interfaces/IPermission.sol` vendored
257
+ under `.sail/contracts/`):
258
+
259
+ ```bash
260
+ forge build
261
+ sailor mandate deploy --contract MyMandate \
262
+ --args '["0xPermissionSigner", ["0xTarget"]]' \
263
+ --attach --sma 0xSafe
264
+ ```
265
+
266
+ `deploy` emits a contract-creation signing request (the owner signs it in the
267
+ browser); the deployed address is read from the receipt and tracked in
268
+ `.sail/state/mandates.json`. `attach` reads the signer nonce, has the owner sign
269
+ a `RegisterPermission` EIP-712 message, then the agent submits
270
+ `kernel.registerPermission` with the exact registration fee. Every command takes
271
+ `--json` for headless agent use; set `SAIL_PASSPHRASE` to unlock the manager key
272
+ non-interactively.
273
+
274
+ `sailor mandate sign` reconciles against the live on-chain `getPermissions()` call
275
+ before building the mandate payload — permissions revoked on-chain are excluded even
276
+ if they remain in the local `.sail/state/mandates.json` (which is an append-only
277
+ historical record and is never modified by the reconciliation).
278
+
279
+ ### GitHub Actions CI
280
+
281
+ The scaffolded `.github/workflows/agent-tick.yml` runs `sailor run --once` on a
282
+ cron schedule using `npm ci` (no pnpm required). Setup:
283
+
284
+ 1. `sailor keys export-ci` copies the encrypted agent wallet to `ci-keystore.json`
285
+ in the project root and allowlists it in `.gitignore`. The geth v3 keystore is
286
+ safe to commit; the raw private key is never exposed.
287
+ 2. Commit `ci-keystore.json`, `.sail/account.json`, and `.sail/mandate.json`.
288
+ 3. Add two repository secrets (Settings Secrets → Actions):
289
+ - `SAIL_PASSPHRASE` the passphrase that encrypts the agent wallet
290
+ - `RPC_URL` — your RPC endpoint
291
+
292
+ The workflow copies `ci-keystore.json` to `.sail/keys/manager.json`, then calls
293
+ `npx sailor run --once` with `SAIL_PASSPHRASE` set so the key is unlocked
294
+ non-interactively. No private key ever appears in the workflow file or in secrets.
295
+
296
+ ---
297
+
298
+ ## Packages
299
+
300
+ Sailor ships as a **single npm package** — the SDK is bundled inside it and exposed via a subpath export:
301
+
302
+ | Package | Contents |
303
+ |---|---|
304
+ | `@sail.money/sailor` | CLI binary, UI server, templates, examples, and SDK |
305
+
306
+ The SDK is available as a subpath export for use in agent code:
307
+
308
+ ```ts
309
+ import type { Agent, AgentContext, Dispatch } from '@sail.money/sailor/sdk'
310
+ ```
311
+
312
+ The SDK is also published separately as `@sail.money/sdk` for projects that consume it independently of the CLI.
313
+
314
+ ### npm (`publish-npm.yml`)
315
+
316
+ Published to the public npm registry under the `@sail.money` scope.
317
+
318
+ | Trigger | Package | Version | dist-tag |
319
+ |---|---|---|---|
320
+ | Tag push (`v*`) | `@sail.money/sailor` | `0.1.0` | `latest` |
321
+ | Manual dispatch | `@dev.sail.money/sailor` | `0.1.0-42` | `dev` |
322
+
323
+ ```bash
324
+ npm install @sail.money/sailor # latest stable (tag push)
325
+ ```
326
+
327
+ For dev builds, the package name changes scope to `@dev.sail.money`. Use an alias so your import paths stay the same:
328
+
329
+ ```bash
330
+ npm install "@sail.money/sailor@npm:@dev.sail.money/sailor@dev"
331
+ ```
332
+
333
+ This installs the latest dev build and makes it available as `@sail.money/sailor` locally — `@sail.money/sailor/sdk` imports continue to work unchanged.
334
+
335
+ ### GitHub Packages (`publish.yml`)
336
+
337
+ Published to GitHub Packages under the `@sail-money` scope for internal testing — no public npm registry required.
338
+
339
+ | Trigger | Package | dist-tag |
340
+ |---|---|---|
341
+ | Merge to `main` | `@sail-money/sailor` | `latest` |
342
+ | Manual dispatch | `@sail-money/sailor-dev` | `dev` |
343
+
344
+ Both builds require an alias since the package scope differs from `@sail.money`:
345
+
346
+ ```bash
347
+ npm install "@sail.money/sailor@npm:@sail-money/sailor@latest" --registry https://npm.pkg.github.com
348
+ npm install "@sail.money/sailor@npm:@sail-money/sailor-dev@dev" --registry https://npm.pkg.github.com
349
+ ```
350
+
351
+ Or pin in `package.json`:
352
+
353
+ ```json
354
+ "dependencies": {
355
+ "@sail.money/sailor": "npm:@sail-money/sailor@latest"
356
+ }
357
+ ```
358
+
359
+ ```json
360
+ "dependencies": {
361
+ "@sail.money/sailor": "npm:@sail-money/sailor-dev@dev"
362
+ }
363
+ ```
364
+
365
+ Either way, `@sail.money/sailor/sdk` imports work unchanged.
366
+
367
+ ---
368
+
369
+ ## Security model
370
+
371
+ - The agent signs dispatches; the kernel evaluates the named permission on every call. A permission returning false or exceeding its gas cap is treated as denial — fail-closed.
372
+ - The Owner key controls the Safe and is never read by Sailor. Mandate signing requires a deliberate action by the permission signer.
373
+ - The manager key is encrypted on disk using geth keystore v3 (scrypt + aes-128-ctr) and is never transmitted.
374
+ - The session can be paused instantly via `sailor session pause` or the dashboard stop button; this does not affect Safe custody.
375
+ - All addresses passed to the CLI are normalized with `getAddress()` (EIP-55 checksum). Mixed-case or lowercase inputs are accepted and canonicalized before any on-chain call or state write.
376
+
377
+ ---
378
+
379
+ ## State of the project
380
+
381
+ Sailor is functional and published as [`@sail.money/sailor`](https://www.npmjs.com/package/@sail.money/sailor) on npm (v0.1.0). The SDK, CLI, keystore, mandate flows, agent runner, and dashboard are implemented and have been exercised end to end.
382
+
383
+ The Sail Protocol trusted core is deployed on six chains — Ethereum, Base, Arbitrum, Unichain, Base Sepolia, and Eth Sepolia — via CREATE2, with every core contract at the same address on every chain. All six run the selective dispatch model with zero fees and are bootstrapped with a genesis allowlist so `createAccount` is usable immediately. These deployments are under an ongoing external audit by [Octane Security](https://octane.security) and are not final — do not use them with funds you are not prepared to lose.
384
+
385
+ Permission templates have not yet been deployed against the current kernel on any chain; `knownTemplates` and `standaloneTemplates` are empty for all six chains in `packages/sdk/src/deployments.ts` and will be populated as templates are deployed and verified against the new kernel.
386
+
387
+ ---
388
+
389
+ ## Deployments
390
+
391
+ All core contracts are deployed at the same address on every supported chain via CREATE2 (commit `1199b33`, 2026-06-09). An SMA created with the same owner, permission signer, manager, fee policy, and salt has the same address on every supported chain.
392
+
393
+ ### Core addresses (identical on all 6 chains)
394
+
395
+ | Contract | Address |
396
+ |---|---|
397
+ | SailKernel | `0x02ABC18B65A328de2e749F56ba79ACF2718a6659` |
398
+ | SailGovernance | `0x7A478118715791728BDE3bc7A4D7ECfdEB89C6EC` |
399
+ | TimelockController | `0xE48Ba8DB6d748adafD13155c3590f62e58a77f56` |
400
+ | MandateFactory | `0x14EDd6c2a56EfC0d71E215ab13094B9AF90543d2` |
401
+ | StandardFeePolicy | `0xe7B5901b839cFFDEd9D4108A22712C8BfdA1D80D` |
402
+ | SafeModuleEnabler | `0x7897Cb53a4be4a2eaAf46D60573C4Fd83b33fE1F` |
403
+ | Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
404
+
405
+ These addresses are bundled in `@sail.money/sailor` and exposed via `getSailDeployment(chainId)` in the SDK. The Protocol repository is the canonical source of truth for deployment details — see [deployments/addresses.md](https://github.com/sail-money/Protocol/blob/main/deployments/addresses.md).
406
+
407
+ ### Supported chains
408
+
409
+ | Chain | Chain ID |
410
+ |---|---|
411
+ | Ethereum | 1 |
412
+ | Base | 8453 |
413
+ | Arbitrum | 42161 |
414
+ | Unichain | 130 |
415
+ | Base Sepolia | 84532 |
416
+ | Eth Sepolia | 11155111 |
417
+
418
+ ---
419
+
420
+ ## Contributing
421
+
422
+ Sailor and Sail Protocol are separate repositories with separate concerns. Protocol questions — SailKernel internals, permission templates, MandateFactory, fee policies — belong in the [SailProtocol](../SailProtocol) repository. Sailor questions — the SDK, CLI, dashboard, and agent templates — belong here.
423
+
424
+ ---
425
+
426
+ ## License
427
+
428
+ MIT