Security and keys for agents
An AI assistant with a signing key can move real assets on your behalf, on the strength of text it read somewhere: a web page, a token description, a chat message. Treat the key as the only thing standing between that text and your wallet, and give the assistant as little as the task needs.
Rules
- Start without a key. Every read tool works without
PRIVATE_KEY: pools, prices, balances, quotes, documentation. A server without a key cannot sign, whatever the assistant is told. - Never paste a private key into a chat, a prompt, a command line or a file the assistant can read. Anything in the conversation is sent to the model provider and kept in transcripts. A key belongs in the server process's environment (see below), nowhere else.
- Use a dedicated wallet. Fund it with what the task needs and no more, and use a key made for
testing while you try workflows on
stage. - Learn on
stage, the test network.ENVIRONMENTisprod(the default) orstage, nothing else. Production writes are real. A server started withoutENVIRONMENTis onprod. - Quote, read the quote, then write. Ask the assistant to run the quote or calculate tool of an action first and to show you the result before it signs anything. Review each write; do not grant blanket approval to a tool in your client's permission settings for a server that can sign.
- Select only the toolsets the task needs. A server with
GALA_TOOLSETS=core,gswaphas no way to create a Launchpad token. Be clear about what this does not do: toolsets narrow the tools, they do not make a toolset read-only.corestill contains the GALA transfer, so only leaving outPRIVATE_KEYmakes the server read-only.
Where a key may live
| Place | Acceptable? |
|---|---|
The server's env block in a configuration file only you can read, for a test wallet | Yes, with care: the file holds a secret in clear text. Do not commit it or sync it. |
An environment variable of your shell, referenced from the client configuration (${NAME} in a project .mcp.json) | Yes: nothing secret is in the file you commit. |
| A secret manager that launches the client with the variable set | Yes. |
| A chat message, a prompt, a README, an example or a shared file | No. |
A tool argument (see set_wallet below) | Avoid. |
set_wallet puts the key in the conversation
The tool gala_launchpad_set_wallet takes the private key as an argument, so calling it means the key
passes through the model and into the transcript. It exists for the case where a person at the keyboard
configures a session on a machine they control. When you can, set PRIVATE_KEY in the server's environment
instead and never call it:
gala_launchpad_has_wallet({})
gala_launchpad_get_wallet({})
Those two report whether a wallet is configured and which address it is, without a key in either call.
Addresses
A GalaChain user is written eth|<40 hex digits> (the checksummed spelling), or client|<id>. The
server also accepts a checksummed 0x<40 hex> and converts it. eth|0x<hex> is refused: it is not an
address form, and the refusal names the correct spelling. When an assistant composes an address, the server
validates it before anything is signed, and a refused address is a prompt to look the address up, not to
try another spelling. Amounts are whole tokens as decimal strings, so '1.5' is one and a half tokens.
What the code enforces
These statements are held by tests of this package, not by this page:
- The environment is
prod(the default) orstage; anything else is refused. Enforced bypackages/gala-mcp-server/src/session/__tests__/session.test.ts("ENVIRONMENT is prod (default) or stage; anything else is refused"). - A malformed key is refused and changes nothing. Enforced by
packages/gala-mcp-server/src/session/__tests__/session.test.ts("refuses a malformed key and changes nothing"). - The server refuses
eth|0x<hex>: its address checks take exactly the forms the Launchpad SDK takes. Enforced bypackages/gala-mcp-server/src/__tests__/address-forms-sync.test.ts("the server address checks take exactly the forms launchpad-sdk takes"). - A toolset that is not selected is never loaded. Enforced by
packages/gala-mcp-server/src/toolsets/__tests__/module-graph.test.ts("module graph per selection"). - Switching the environment changes nothing if any client cannot be built. Enforced by
packages/gala-mcp-server/src/session/__tests__/session.test.ts("one participant that cannot be built refuses the switch: the others are aborted, nothing changes").
If a key was exposed
Treat it as lost: move the funds to a new wallet and stop using the old key. Removing it from a file or a transcript does not undo that it was sent.