@suisei-mcp/agent-signer 0.1.0
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.
- package/README.md +103 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/keystore.d.ts +38 -0
- package/dist/keystore.js +71 -0
- package/dist/keystore.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @suisei-mcp/agent-signer
|
|
2
|
+
|
|
3
|
+
The non-custodial signer for a **Tier-1 Sui agent wallet**.
|
|
4
|
+
|
|
5
|
+
[`@suisei-mcp/mcp`](../mcp) builds *unsigned* transaction
|
|
6
|
+
bytes and never holds a key. This package is the one piece that does: it
|
|
7
|
+
generates the agent's keypair, stores it **encrypted on your machine**, and
|
|
8
|
+
signs builder bytes. The plaintext key is created here, used here, and
|
|
9
|
+
**never crosses a process boundary** — it never travels through an MCP
|
|
10
|
+
response or into an LLM's context.
|
|
11
|
+
|
|
12
|
+
## What "Tier-1 agent wallet" means
|
|
13
|
+
|
|
14
|
+
A real Sui wallet the agent fully controls, funded with a small allowance.
|
|
15
|
+
The agent can stake, transfer, swap — anything — but only up to its
|
|
16
|
+
balance. Blast radius = what you fund. Refill to extend, sweep to revoke.
|
|
17
|
+
Your main wallet is never touched: the agent has its own freshly-generated
|
|
18
|
+
key, and your owner key stays in your real wallet.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @suisei-mcp/agent-signer
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Use
|
|
27
|
+
|
|
28
|
+
Set a passphrase (encrypts the key at rest) and create the wallet:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export AGENT_WALLET_PASSPHRASE="something-long-and-private"
|
|
32
|
+
agent-signer create
|
|
33
|
+
# -> { "address": "0x…", "path": "~/.suisei/agent-wallet.json" }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then the loop with Claude + the MCP:
|
|
37
|
+
|
|
38
|
+
1. Ask the agent to fund it: it calls `agent_wallet_fund` (owner-signed) so
|
|
39
|
+
your real wallet sends SUI to the agent address.
|
|
40
|
+
2. Ask the agent to act, e.g. *"stake 1 SUI to the top validator from my
|
|
41
|
+
agent wallet"* — it builds the tx and gives you `tx_bytes_base64`.
|
|
42
|
+
3. Sign with the agent key:
|
|
43
|
+
```bash
|
|
44
|
+
agent-signer sign <tx_bytes_base64>
|
|
45
|
+
# -> { "signature": "…" }
|
|
46
|
+
```
|
|
47
|
+
4. Tell the agent to `sui_execute_signed_tx` with that signature.
|
|
48
|
+
|
|
49
|
+
To revoke, ask the agent for `agent_wallet_sweep` (drains the wallet back to
|
|
50
|
+
you), sign it, submit, and stop funding.
|
|
51
|
+
|
|
52
|
+
## You own the key — back it up or move it
|
|
53
|
+
|
|
54
|
+
The wallet isn't locked inside this tool. The private key is yours,
|
|
55
|
+
encrypted under your passphrase, and you can take it out anytime:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
agent-signer export
|
|
59
|
+
# -> { "address": "0x…", "secret_key": "suiprivkey1…", "warning": "…" }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That `suiprivkey1…` is a standard Sui secret. Import it into **Sui Wallet**,
|
|
63
|
+
**Suiet**, or the CLI (`sui keytool import "<suiprivkey1…>" ed25519`) and
|
|
64
|
+
you have the same wallet there. Back it up offline — anyone holding it
|
|
65
|
+
controls the wallet.
|
|
66
|
+
|
|
67
|
+
Bring your own key instead of generating one:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
agent-signer import "suiprivkey1…"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Because the key is exportable, losing the keystore file isn't fatal **if
|
|
74
|
+
you backed up the export**. If you didn't and you lose the file or
|
|
75
|
+
passphrase, the agent wallet is gone — but it only ever holds an allowance,
|
|
76
|
+
so create a fresh one and refund. Your owner wallet is untouched.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
| Env / flag | Default | Meaning |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `AGENT_WALLET_PASSPHRASE` / `--passphrase` | — (required) | Encrypts/decrypts the key |
|
|
83
|
+
| `AGENT_WALLET_PATH` / `--path` | `~/.suisei/agent-wallet.json` | Keystore location |
|
|
84
|
+
|
|
85
|
+
## Security model
|
|
86
|
+
|
|
87
|
+
- **Ed25519** key, sealed with **AES-256-GCM** under a **scrypt**-derived
|
|
88
|
+
key (N=32768). Keystore written `0600`.
|
|
89
|
+
- Wrong passphrase → decryption fails closed (GCM auth tag).
|
|
90
|
+
- `create`/`address` emit only the public address; `sign` emits only a
|
|
91
|
+
signature. The raw secret is revealed only by an explicit `export` (with
|
|
92
|
+
a warning) — never incidentally, and never to the MCP or an agent.
|
|
93
|
+
- Lose the keystore or passphrase and the agent wallet is gone — by design
|
|
94
|
+
it holds only an allowance, so create a fresh one and refund. Your owner
|
|
95
|
+
wallet (in your real wallet app) is unaffected.
|
|
96
|
+
|
|
97
|
+
This is **Tier 1** of the agent-wallet design. On-chain policy limits
|
|
98
|
+
(Tier 2) and a multisig co-signer (Tier 3) layer on top — see
|
|
99
|
+
[`docs/AGENT_WALLET_DESIGN.md`](../../docs/AGENT_WALLET_DESIGN.md).
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createWallet, walletAddress, signTxBytes, exportSecret, importWallet, defaultPath, } from './index.js';
|
|
3
|
+
/**
|
|
4
|
+
* agent-signer CLI — the manual signing path for a Tier-1 agent wallet.
|
|
5
|
+
*
|
|
6
|
+
* agent-signer create [--overwrite] generate + encrypt a new agent key
|
|
7
|
+
* agent-signer import <suiprivkey> seal an existing key as the wallet
|
|
8
|
+
* agent-signer export reveal the raw key (backup / import elsewhere)
|
|
9
|
+
* agent-signer address print the agent wallet address
|
|
10
|
+
* agent-signer sign <txBytesBase64> sign builder bytes -> base64 signature
|
|
11
|
+
*
|
|
12
|
+
* Passphrase comes from AGENT_WALLET_PASSPHRASE (preferred) or --passphrase.
|
|
13
|
+
* Keystore path from AGENT_WALLET_PATH or --path (default ~/.suisei/agent-wallet.json).
|
|
14
|
+
*
|
|
15
|
+
* Typical loop with Claude + the MCP:
|
|
16
|
+
* 1. agent builds a tx -> tx_bytes_base64
|
|
17
|
+
* 2. agent-signer sign <tx_bytes_base64> -> copy the "signature"
|
|
18
|
+
* 3. tell the agent to sui_execute_signed_tx with that signature
|
|
19
|
+
*/
|
|
20
|
+
function flag(name) {
|
|
21
|
+
const i = process.argv.indexOf(`--${name}`);
|
|
22
|
+
return i !== -1 ? process.argv[i + 1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
function has(name) {
|
|
25
|
+
return process.argv.includes(`--${name}`);
|
|
26
|
+
}
|
|
27
|
+
function getPassphrase() {
|
|
28
|
+
const p = process.env.AGENT_WALLET_PASSPHRASE ?? flag('passphrase');
|
|
29
|
+
if (!p) {
|
|
30
|
+
fail('No passphrase. Set AGENT_WALLET_PASSPHRASE or pass --passphrase <value>. ' +
|
|
31
|
+
'This encrypts the agent key at rest.');
|
|
32
|
+
}
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
function getPath() {
|
|
36
|
+
return flag('path') ?? defaultPath();
|
|
37
|
+
}
|
|
38
|
+
function out(obj) {
|
|
39
|
+
process.stdout.write(JSON.stringify(obj, null, 2) + '\n');
|
|
40
|
+
}
|
|
41
|
+
function fail(msg) {
|
|
42
|
+
process.stderr.write(`error: ${msg}\n`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
async function main() {
|
|
46
|
+
const cmd = process.argv[2];
|
|
47
|
+
switch (cmd) {
|
|
48
|
+
case 'create': {
|
|
49
|
+
const res = createWallet({
|
|
50
|
+
passphrase: getPassphrase(),
|
|
51
|
+
path: getPath(),
|
|
52
|
+
overwrite: has('overwrite'),
|
|
53
|
+
});
|
|
54
|
+
out({
|
|
55
|
+
created: true,
|
|
56
|
+
address: res.address,
|
|
57
|
+
path: res.path,
|
|
58
|
+
next_step: 'Fund it with the agent_wallet_fund MCP tool (owner-signed), then the agent can spend up to its balance.',
|
|
59
|
+
});
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'import': {
|
|
63
|
+
const secret = process.argv[3];
|
|
64
|
+
if (!secret || secret.startsWith('--')) {
|
|
65
|
+
fail('Usage: agent-signer import <suiprivkey1...>');
|
|
66
|
+
}
|
|
67
|
+
const res = importWallet({
|
|
68
|
+
passphrase: getPassphrase(),
|
|
69
|
+
secret,
|
|
70
|
+
path: getPath(),
|
|
71
|
+
overwrite: has('overwrite'),
|
|
72
|
+
});
|
|
73
|
+
out({ imported: true, address: res.address, path: res.path });
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'export': {
|
|
77
|
+
const { address, secretBech32 } = exportSecret({
|
|
78
|
+
passphrase: getPassphrase(),
|
|
79
|
+
path: getPath(),
|
|
80
|
+
});
|
|
81
|
+
out({
|
|
82
|
+
address,
|
|
83
|
+
secret_key: secretBech32,
|
|
84
|
+
warning: 'This is the full private key. Anyone with it controls the wallet. Back it up offline; import into Sui Wallet / Suiet / `sui keytool import` to use it elsewhere.',
|
|
85
|
+
});
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case 'address': {
|
|
89
|
+
out({ address: walletAddress({ passphrase: getPassphrase(), path: getPath() }) });
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'sign': {
|
|
93
|
+
const txBytesBase64 = process.argv[3];
|
|
94
|
+
if (!txBytesBase64 || txBytesBase64.startsWith('--')) {
|
|
95
|
+
fail('Usage: agent-signer sign <txBytesBase64>');
|
|
96
|
+
}
|
|
97
|
+
const res = await signTxBytes({
|
|
98
|
+
passphrase: getPassphrase(),
|
|
99
|
+
txBytesBase64,
|
|
100
|
+
path: getPath(),
|
|
101
|
+
});
|
|
102
|
+
out({
|
|
103
|
+
address: res.address,
|
|
104
|
+
signature: res.signature,
|
|
105
|
+
next_step: 'Submit with the sui_execute_signed_tx MCP tool: pass the same tx bytes and this signature.',
|
|
106
|
+
});
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
process.stdout.write([
|
|
111
|
+
'agent-signer — non-custodial signer for a Tier-1 Sui agent wallet',
|
|
112
|
+
'',
|
|
113
|
+
'Commands:',
|
|
114
|
+
' create [--overwrite] generate + encrypt a new agent key',
|
|
115
|
+
' import <suiprivkey> seal an existing key as the wallet',
|
|
116
|
+
' export reveal the raw key (backup / import elsewhere)',
|
|
117
|
+
' address print the agent wallet address',
|
|
118
|
+
' sign <txBytesBase64> sign builder bytes -> base64 signature',
|
|
119
|
+
'',
|
|
120
|
+
'Auth: AGENT_WALLET_PASSPHRASE (or --passphrase), AGENT_WALLET_PATH (or --path)',
|
|
121
|
+
'',
|
|
122
|
+
].join('\n'));
|
|
123
|
+
if (cmd && cmd !== 'help' && cmd !== '--help')
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
main().catch((e) => fail(e instanceof Error ? e.message : String(e)));
|
|
128
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
2
|
+
/**
|
|
3
|
+
* @suisei-mcp/agent-signer — the non-custodial signer for a Tier-1 Sui agent
|
|
4
|
+
* wallet.
|
|
5
|
+
*
|
|
6
|
+
* The MCP toolkit builds unsigned tx bytes and never holds a key. This is
|
|
7
|
+
* the only piece that does: it generates the agent keypair, stores it
|
|
8
|
+
* encrypted on the user's machine, and signs tx bytes. The plaintext key
|
|
9
|
+
* is created here, used here, and never returned across a process boundary
|
|
10
|
+
* — in particular it never travels through an MCP response or an LLM
|
|
11
|
+
* context.
|
|
12
|
+
*
|
|
13
|
+
* Pair the signature this produces with sui_execute_signed_tx to submit.
|
|
14
|
+
*/
|
|
15
|
+
export interface CreateResult {
|
|
16
|
+
address: string;
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
/** Generate a fresh agent keypair and persist it encrypted. Returns the address only. */
|
|
20
|
+
export declare function createWallet(opts: {
|
|
21
|
+
passphrase: string;
|
|
22
|
+
path?: string;
|
|
23
|
+
overwrite?: boolean;
|
|
24
|
+
}): CreateResult;
|
|
25
|
+
/** Load and decrypt the agent keypair. Plaintext stays inside this process. */
|
|
26
|
+
export declare function loadKeypair(opts: {
|
|
27
|
+
passphrase: string;
|
|
28
|
+
path?: string;
|
|
29
|
+
}): Ed25519Keypair;
|
|
30
|
+
/** The agent wallet address, without decrypting the key (reads the stored address). */
|
|
31
|
+
export declare function walletAddress(opts: {
|
|
32
|
+
passphrase: string;
|
|
33
|
+
path?: string;
|
|
34
|
+
}): string;
|
|
35
|
+
/**
|
|
36
|
+
* Reveal the raw bech32 `suiprivkey...` secret so the user can back it up
|
|
37
|
+
* or import the agent wallet into a standard wallet (Sui Wallet, Suiet,
|
|
38
|
+
* `sui keytool import`). This is the user's escape hatch — it proves they,
|
|
39
|
+
* not us, own the key. Handle the output carefully: anyone with this string
|
|
40
|
+
* controls the wallet.
|
|
41
|
+
*/
|
|
42
|
+
export declare function exportSecret(opts: {
|
|
43
|
+
passphrase: string;
|
|
44
|
+
path?: string;
|
|
45
|
+
}): {
|
|
46
|
+
address: string;
|
|
47
|
+
secretBech32: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Import an existing key as the agent wallet: seal a bech32 `suiprivkey...`
|
|
51
|
+
* (or raw 32-byte) secret into the keystore. Lets a user bring their own
|
|
52
|
+
* key instead of generating one.
|
|
53
|
+
*/
|
|
54
|
+
export declare function importWallet(opts: {
|
|
55
|
+
passphrase: string;
|
|
56
|
+
secret: string;
|
|
57
|
+
path?: string;
|
|
58
|
+
overwrite?: boolean;
|
|
59
|
+
}): CreateResult;
|
|
60
|
+
/**
|
|
61
|
+
* Sign base64 tx bytes (from any @suisei-mcp/mcp builder tool) with the agent
|
|
62
|
+
* key. Returns the base64 Sui signature to pass to sui_execute_signed_tx.
|
|
63
|
+
*/
|
|
64
|
+
export declare function signTxBytes(opts: {
|
|
65
|
+
passphrase: string;
|
|
66
|
+
txBytesBase64: string;
|
|
67
|
+
path?: string;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
address: string;
|
|
70
|
+
signature: string;
|
|
71
|
+
}>;
|
|
72
|
+
export { defaultPath } from './keystore.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
2
|
+
import { defaultPath, saveSecret, loadSecret, keystoreExists } from './keystore.js';
|
|
3
|
+
/** Generate a fresh agent keypair and persist it encrypted. Returns the address only. */
|
|
4
|
+
export function createWallet(opts) {
|
|
5
|
+
const path = opts.path ?? defaultPath();
|
|
6
|
+
if (keystoreExists(path) && !opts.overwrite) {
|
|
7
|
+
throw new Error(`An agent wallet already exists at ${path}. Pass overwrite to replace it (you will lose the old key).`);
|
|
8
|
+
}
|
|
9
|
+
const kp = new Ed25519Keypair();
|
|
10
|
+
const address = kp.getPublicKey().toSuiAddress();
|
|
11
|
+
saveSecret({ path, passphrase: opts.passphrase, address, secretBech32: kp.getSecretKey() });
|
|
12
|
+
return { address, path };
|
|
13
|
+
}
|
|
14
|
+
/** Load and decrypt the agent keypair. Plaintext stays inside this process. */
|
|
15
|
+
export function loadKeypair(opts) {
|
|
16
|
+
const path = opts.path ?? defaultPath();
|
|
17
|
+
const { secretBech32 } = loadSecret({ path, passphrase: opts.passphrase });
|
|
18
|
+
return Ed25519Keypair.fromSecretKey(secretBech32);
|
|
19
|
+
}
|
|
20
|
+
/** The agent wallet address, without decrypting the key (reads the stored address). */
|
|
21
|
+
export function walletAddress(opts) {
|
|
22
|
+
const path = opts.path ?? defaultPath();
|
|
23
|
+
return loadSecret({ path, passphrase: opts.passphrase }).address;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reveal the raw bech32 `suiprivkey...` secret so the user can back it up
|
|
27
|
+
* or import the agent wallet into a standard wallet (Sui Wallet, Suiet,
|
|
28
|
+
* `sui keytool import`). This is the user's escape hatch — it proves they,
|
|
29
|
+
* not us, own the key. Handle the output carefully: anyone with this string
|
|
30
|
+
* controls the wallet.
|
|
31
|
+
*/
|
|
32
|
+
export function exportSecret(opts) {
|
|
33
|
+
const path = opts.path ?? defaultPath();
|
|
34
|
+
return loadSecret({ path, passphrase: opts.passphrase });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Import an existing key as the agent wallet: seal a bech32 `suiprivkey...`
|
|
38
|
+
* (or raw 32-byte) secret into the keystore. Lets a user bring their own
|
|
39
|
+
* key instead of generating one.
|
|
40
|
+
*/
|
|
41
|
+
export function importWallet(opts) {
|
|
42
|
+
const path = opts.path ?? defaultPath();
|
|
43
|
+
if (keystoreExists(path) && !opts.overwrite) {
|
|
44
|
+
throw new Error(`An agent wallet already exists at ${path}. Pass overwrite to replace it (you will lose the old key).`);
|
|
45
|
+
}
|
|
46
|
+
let kp;
|
|
47
|
+
try {
|
|
48
|
+
kp = Ed25519Keypair.fromSecretKey(opts.secret.trim());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error('Invalid secret key. Expected a bech32 "suiprivkey1..." string.');
|
|
52
|
+
}
|
|
53
|
+
const address = kp.getPublicKey().toSuiAddress();
|
|
54
|
+
// Re-serialize through getSecretKey() so storage is always canonical bech32.
|
|
55
|
+
saveSecret({ path, passphrase: opts.passphrase, address, secretBech32: kp.getSecretKey() });
|
|
56
|
+
return { address, path };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sign base64 tx bytes (from any @suisei-mcp/mcp builder tool) with the agent
|
|
60
|
+
* key. Returns the base64 Sui signature to pass to sui_execute_signed_tx.
|
|
61
|
+
*/
|
|
62
|
+
export async function signTxBytes(opts) {
|
|
63
|
+
const kp = loadKeypair({ passphrase: opts.passphrase, path: opts.path });
|
|
64
|
+
const bytes = new Uint8Array(Buffer.from(opts.txBytesBase64, 'base64'));
|
|
65
|
+
const { signature } = await kp.signTransaction(bytes);
|
|
66
|
+
return { address: kp.getPublicKey().toSuiAddress(), signature };
|
|
67
|
+
}
|
|
68
|
+
export { defaultPath } from './keystore.js';
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted-at-rest keystore for the agent wallet's Ed25519 secret key.
|
|
3
|
+
*
|
|
4
|
+
* The secret is sealed with AES-256-GCM under a key derived from a
|
|
5
|
+
* passphrase via scrypt. We store only ciphertext + the public address.
|
|
6
|
+
* Nothing here ever returns a plaintext key to a caller other than the
|
|
7
|
+
* in-process signer, and the file is written 0600. The key never leaves
|
|
8
|
+
* the user's machine and never enters an agent/LLM context.
|
|
9
|
+
*/
|
|
10
|
+
export interface KeystoreFile {
|
|
11
|
+
version: 1;
|
|
12
|
+
address: string;
|
|
13
|
+
scheme: 'ed25519';
|
|
14
|
+
kdf: 'scrypt';
|
|
15
|
+
salt: string;
|
|
16
|
+
iv: string;
|
|
17
|
+
auth_tag: string;
|
|
18
|
+
ciphertext: string;
|
|
19
|
+
created_at: string;
|
|
20
|
+
}
|
|
21
|
+
/** Default keystore path; override with AGENT_WALLET_PATH. */
|
|
22
|
+
export declare function defaultPath(): string;
|
|
23
|
+
/** Encrypt a bech32 `suiprivkey...` string into a keystore file on disk. */
|
|
24
|
+
export declare function saveSecret(opts: {
|
|
25
|
+
path: string;
|
|
26
|
+
passphrase: string;
|
|
27
|
+
address: string;
|
|
28
|
+
secretBech32: string;
|
|
29
|
+
}): void;
|
|
30
|
+
/** Decrypt and return the bech32 `suiprivkey...` string. Throws on bad passphrase. */
|
|
31
|
+
export declare function loadSecret(opts: {
|
|
32
|
+
path: string;
|
|
33
|
+
passphrase: string;
|
|
34
|
+
}): {
|
|
35
|
+
address: string;
|
|
36
|
+
secretBech32: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function keystoreExists(path: string): boolean;
|
package/dist/keystore.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { randomBytes, scryptSync, createCipheriv, createDecipheriv, } from 'node:crypto';
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
const SCRYPT_N = 1 << 15; // 32768
|
|
6
|
+
const SCRYPT_PARAMS = { N: SCRYPT_N, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
7
|
+
/** Default keystore path; override with AGENT_WALLET_PATH. */
|
|
8
|
+
export function defaultPath() {
|
|
9
|
+
return process.env.AGENT_WALLET_PATH ?? join(homedir(), '.suisei', 'agent-wallet.json');
|
|
10
|
+
}
|
|
11
|
+
function deriveKey(passphrase, salt) {
|
|
12
|
+
return scryptSync(passphrase, salt, 32, SCRYPT_PARAMS);
|
|
13
|
+
}
|
|
14
|
+
/** Encrypt a bech32 `suiprivkey...` string into a keystore file on disk. */
|
|
15
|
+
export function saveSecret(opts) {
|
|
16
|
+
const salt = randomBytes(16);
|
|
17
|
+
const iv = randomBytes(12);
|
|
18
|
+
const key = deriveKey(opts.passphrase, salt);
|
|
19
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
20
|
+
const ciphertext = Buffer.concat([
|
|
21
|
+
cipher.update(Buffer.from(opts.secretBech32, 'utf8')),
|
|
22
|
+
cipher.final(),
|
|
23
|
+
]);
|
|
24
|
+
const authTag = cipher.getAuthTag();
|
|
25
|
+
const file = {
|
|
26
|
+
version: 1,
|
|
27
|
+
address: opts.address,
|
|
28
|
+
scheme: 'ed25519',
|
|
29
|
+
kdf: 'scrypt',
|
|
30
|
+
salt: salt.toString('hex'),
|
|
31
|
+
iv: iv.toString('hex'),
|
|
32
|
+
auth_tag: authTag.toString('hex'),
|
|
33
|
+
ciphertext: ciphertext.toString('hex'),
|
|
34
|
+
created_at: new Date().toISOString(),
|
|
35
|
+
};
|
|
36
|
+
mkdirSync(dirname(opts.path), { recursive: true });
|
|
37
|
+
writeFileSync(opts.path, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
38
|
+
try {
|
|
39
|
+
chmodSync(opts.path, 0o600);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// best-effort on platforms without POSIX perms
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Decrypt and return the bech32 `suiprivkey...` string. Throws on bad passphrase. */
|
|
46
|
+
export function loadSecret(opts) {
|
|
47
|
+
if (!existsSync(opts.path)) {
|
|
48
|
+
throw new Error(`No agent wallet at ${opts.path}. Run "agent-signer create" first.`);
|
|
49
|
+
}
|
|
50
|
+
const file = JSON.parse(readFileSync(opts.path, 'utf8'));
|
|
51
|
+
if (file.version !== 1)
|
|
52
|
+
throw new Error(`Unsupported keystore version ${file.version}.`);
|
|
53
|
+
const key = deriveKey(opts.passphrase, Buffer.from(file.salt, 'hex'));
|
|
54
|
+
const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(file.iv, 'hex'));
|
|
55
|
+
decipher.setAuthTag(Buffer.from(file.auth_tag, 'hex'));
|
|
56
|
+
let secretBech32;
|
|
57
|
+
try {
|
|
58
|
+
secretBech32 = Buffer.concat([
|
|
59
|
+
decipher.update(Buffer.from(file.ciphertext, 'hex')),
|
|
60
|
+
decipher.final(),
|
|
61
|
+
]).toString('utf8');
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error('Decryption failed — wrong passphrase or corrupted keystore.');
|
|
65
|
+
}
|
|
66
|
+
return { address: file.address, secretBech32 };
|
|
67
|
+
}
|
|
68
|
+
export function keystoreExists(path) {
|
|
69
|
+
return existsSync(path);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=keystore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keystore.js","sourceRoot":"","sources":["../src/keystore.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@suisei-mcp/agent-signer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local, non-custodial signer for a Tier-1 Sui agent wallet. Generates and encrypts an agent keypair on the user's machine and signs tx bytes from @suisei-mcp/mcp. The key never leaves the host and never enters an agent's context.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"agent-signer": "dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"sui",
|
|
28
|
+
"agent",
|
|
29
|
+
"wallet",
|
|
30
|
+
"signer",
|
|
31
|
+
"non-custodial",
|
|
32
|
+
"mcp"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@mysten/sui": "^1.45.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.9.0",
|
|
42
|
+
"typescript": "^5.6.3"
|
|
43
|
+
}
|
|
44
|
+
}
|