API authentication and TLS
The node protects its owner-facing surfaces with HTTP Basic against a secret file on disk. The wallet Owner API v3 uses a token obtained with the wallet password. The foreign surfaces take no credential, because an interactive transfer needs the receiving side reachable by a stranger. TLS termination is left to the operator.
The surfaces
| Surface | Default port | Credential | Who is meant to call it |
|---|---|---|---|
Node /v1/* | 3413 | Basic, api_secret_path, across the whole prefix | You, from the same machine |
Node /v2/owner | 3413 | Basic, api_secret_path | You, from the same machine |
Node /v2/foreign | 3413 | Optional Basic, foreign_api_secret_path | Anything that reads the chain |
Wallet Owner /v3/owner | 3420 | Token from open_wallet, inside the encrypted envelope | You, from the same machine |
Wallet Owner /v2/owner | 3420 | None. Superseded by /v3/owner | Nothing. Bind the listener to loopback |
Wallet Foreign /v2/foreign | 3415 | None | A counterparty paying you |
The wallet's Foreign API receives a slate, returns a partially signed one, builds a coinbase for a miner configured to pay this wallet, and reports its version. Restrict access to it at the proxy.
The node's /v1 credential covers the whole prefix, including the chain reads that are open on
/v2/foreign. See the v1 REST surface.
api_secret_path names a different file in each binary's configuration. Both node surfaces read the
node's, which is .api_secret.
Owner access on the wallet
/v3/owner is gated on a token, not on an HTTP credential. Every method in the v3 trait takes one
(api/src/owner_rpc_s.rs:78), and getting one means completing the
ECDH handshake and then calling open_wallet with the wallet password. The password is the gate. See
the encrypted handshake.
The first call establishes the shared key and carries no credential:
curl -s \
-d '{"jsonrpc":"2.0","method":"init_secure_api","params":{"ecdh_pubkey":"<your compressed public key, hex>"},"id":1}' \
http://127.0.0.1:3420/v3/owner
On top of that credential, every method on /v3/owner takes a token, and a token comes from
open_wallet, which needs the wallet password. owner_api_listen_interface defaults to 127.0.0.1;
for remote access, tunnel to loopback. The listener is
controller/src/controller.rs:123.
The full method list, the encrypted envelope, and which calls move value are on the wallet Owner API page.
The node credential
The node's /v1 prefix and /v2/owner use HTTP Basic with the username literally epic and a secret
file's contents as the password:
Authorization: Basic base64("epic:" + <secret file contents>)
The node creates ~/.epic/<network>/.api_secret on first run and points api_secret_path at it, so
this works without configuration:
curl -u epic:$(cat ~/.epic/main/.api_secret) \
-d '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}' \
http://127.0.0.1:3413/v2/owner
Secret files hold one 20-character random alphanumeric string with no trailing newline
(config/src/config.rs:84), and the comparison is constant time
(api/src/auth.rs:74). The realm is EpicAPI on the owner surface and
EpicForeignAPI on the foreign one. A rejected call, on either binary, returns 401 with a
WWW-Authenticate header and a JSON-RPC error body:
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Unauthorized"}, "id": null}
- With
api_secret_pathcommented out, the listener runs with no credential. - An empty
.api_secretis regenerated with a new value rather than read as "no secret", so emptying the file does not switch authentication off (config/src/config.rs:96). OPTIONSis passed through before the credential check, which is what makes CORS preflight work.
Optional credential on the node's foreign surface
foreign_api_secret_path makes the node's /v2/foreign require a credential too. Create the secret
yourself and give it an absolute path:
head -c 40 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 20 > ~/.epic/main/.foreign_api_secret
[server]
foreign_api_secret_path = "/home/you/.epic/main/.foreign_api_secret"
The realm is EpicForeignAPI and the username is still epic. Verify with:
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:3413/v2/foreign
# 401 once the secret is in place
The credential the wallet presents to the node
node_api_secret_path points the other way: it is what the wallet sends when calling the node, and it
defaults to <wallet dir>/.api_secret. Left at defaults, node and wallet share ~/.epic/<network>/
and resolve to the same file, so they agree with no configuration. Change the node's secret, move
either directory, or point the wallet at another node, and this is the key to update.
TLS
Both binaries take the same pair, both commented out by default:
tls_certificate_file = "/path/to/fullchain.pem"
tls_certificate_key = "/path/to/privkey.pem"
Setting the certificate switches the listener from HTTP to HTTPS. Setting it without the key is a startup error. Certificates are read as a PEM chain; keys may be PKCS#1, PKCS#8 or SEC1 PEM. A file that cannot be opened, cannot be parsed, or does not match its certificate stops the process rather than falling back to HTTP, so a bad pair fails loudly at startup.
The bundled epic client subcommands address the node over http://
(src/bin/cmd/client.rs:160). Use curl against a TLS listener.