Skip to content

Secrets Management

Polygolem and go-bot operate on a least-persistence model for secrets. Nothing is written to disk unless it’s statically provisioned by the operator. The private key is the only mandatory secret and it lives exclusively in the environment.

The Four Secrets Tiers

TierSecretSourcePersisted?Why
1Private keyPOLYMARKET_PRIVATE_KEY envNever to any fileSigns everything. Only in env, never argv, never stdout, never disk.
2Builder credentialspolymarket.com/settings?tab=builderYes.live-secrets.jsonLong-lived, static. Avoids operator re-typing on restart.
3CLOB API keyDerived from private keyNo — regenerateDeterministic from the key. Regeneration costs nothing.
4Deposit wallet addressDerived via CREATE2 from EOACache.live-secrets.jsonDeterministic. Cached for audit trail and display.

Tier 1 — Private Key

Terminal window
# The ONLY place the key lives
export POLYMARKET_PRIVATE_KEY="0x..."

Rules:

  • Never written to any file by polygolem or go-bot.
  • Never appears in command arguments (/proc/*/cmdline).
  • Passed to polygolem subprocess via env only.
  • Redacted in all logs: 0xa1b2...c3d4.
  • If missing from env at startup → hard error, no fallback.

Tier 2 — Builder Credentials

Builder credentials are obtained from polymarket.com/settings?tab=builder. They are an API key, secret, and passphrase tied to your Polymarket account. The relayer uses them to authorize WALLET-CREATE and WALLET batch submissions.

Startup Resolution Order

1. POLYMARKET_BUILDER_API_KEY env var → use it
└─ if not set: .live-secrets.json → read from file
└─ if not found: error → "builder creds required"

The same priority applies to POLYMARKET_BUILDER_SECRET and POLYMARKET_BUILDER_PASSPHRASE.

Persistence Format

go-bot/.live-secrets.json (.gitignored, chmod 600):

{
"eoa_address": "0xAb12...",
"deposit_wallet_address": "0xCd34...",
"builder_api_key": "550e8400-e29b-41d4-a716-446655440000",
"builder_secret": "REDACTED_IN_LOGS_ONLY",
"builder_passphrase": "REDACTED_IN_LOGS_ONLY",
"created_at": "2026-05-07T12:00:00Z"
}

The builder_secret and builder_passphrase values are stored in plaintext on disk but are never logged. All log output shows REDACTED_IN_LOGS_ONLY for these fields.

Writing Secrets on First Run

Terminal window
# Option A: Provide via env on first run, go-bot persists automatically
POLYMARKET_PRIVATE_KEY="0x..." \
POLYMARKET_BUILDER_API_KEY="550e8400-e29b-41d4-a716-446655440000" \
POLYMARKET_BUILDER_SECRET="..." \
POLYMARKET_BUILDER_PASSPHRASE="..." \
go-bot live
# On restart, only the private key is needed:
POLYMARKET_PRIVATE_KEY="0x..." go-bot live

Tier 3 — CLOB API Key

Generated by polygolem clob create-api-key. The command signs the CLOB L1 EIP-712 auth payload with the EOA private key, calls POST /auth/api-key, and falls back to GET /auth/derive-api-key when credentials already exist.

This means:

  • No need to persist in normal bot operation — create or derive on startup.
  • The private key stays local; only the L1 auth signature is sent upstream.
  • The resulting key is used for L2-authenticated CLOB API calls.
// In go-bot: regenerated at startup, never written to disk
apiKey, err := cli.EnsureAPIKey(ctx)

Tier 4 — Deposit Wallet Address

Derived via CREATE2 from the EOA address. Completely deterministic — the same EOA always produces the same deposit wallet address.

Cached in .live-secrets.json for:

  • Faster startup (skip recomputation — small win)
  • Audit trail (which address did this bot trade with?)
  • Cross-check on restart (derived must equal persisted — mismatch = wrong key)