Documentation

How CopyForge works

Architecture, infrastructure, security model, and production specs — everything under the hood.

Overview

How CopyForge works

CopyForge is a non-custodial copytrading platform built on Polymarket. When you subscribe and connect a trading wallet, a dedicated execution process begins mirroring every trade placed by the traders you follow — in real-time, with no manual intervention required.

The system is designed around three concerns: speed (signals delivered in under 10ms via an in-memory queue), cost efficiency (shared market data means N clients consume the same API bandwidth as 1), and security (your private key never leaves your device unencrypted and is isolated inside its own sandboxed container).

~0ms

signal delivery lag

10s

max trade detection lag

API calls per N clients

AES-256

key encryption at rest

Architecture at a glance

A shared market data layer polls Polymarket once for all tracked traders and writes a local cache. A signal detection process reads that cache, identifies new trades, and pushes signals into per-client queues. Each client's execution worker wakes up the instant a signal arrives and places the order — no polling delay, no shared execution path between clients.

Market data layer
Polls Polymarket once — shared across all clients
Signal engine
Detects new trades, deduplicates, fans out to client queues
Execution workers
One per client — isolated, instant wake-up on signal arrival
Polymarket CLOB
Limit or market order placed on your behalf

Infrastructure

Containerised, isolated, self-healing

Every component runs as an isolated container. Shared services run once regardless of how many clients are active. Each client's execution worker runs in its own container with no access to other clients' credentials or state. All containers restart automatically on crash and start on boot without manual intervention.

Message queueInfrastructure

In-memory store for signal queues, deduplication sets, and execution state. No persistence — ephemeral by design. Capped at 256 MB with LRU eviction. Signals trimmed to the last 500 per client.

in-memoryLRU eviction256 MB cap
Market data serviceShared

Polls Polymarket trade history and open positions for every tracked trader. Writes results to a shared local cache using atomic file replacement. Automatically discovers new traders added by any client.

shared cacheatomic writesrate-limited
Signal engineShared

Reads the market data cache, detects new trades via a deduplication store, aggregates same-market activity into single signals, and pushes to each following client's queue. Seeds all existing trades on first run to prevent re-copying after restart.

dedupfan-outseeded on start
Execution workerPer-Client

One container per trading account. Blocks on the queue until a signal arrives, sizes the order according to the client's configured mode, and places it on Polymarket. Subscribes to a real-time fill stream for instant confirmation.

isolatedinstant wake-upfill stream

Isolation model

Shared services (market data, signal engine) mount client configurations read-only and write only to the shared cache layer. Each execution worker mounts only its own credentials — no cross-client file access is possible at the container level. All source volumes are mounted read-only; the only writable mount per client is its own isolated data directory.

New client provisioning

When a client completes the setup flow, their configuration and encrypted credentials are written to an isolated directory, a new execution worker is registered in the container orchestration layer, and the container starts immediately. No restart of existing services, no downtime for other clients.

Signal Pipeline

From trader action to your order

The pipeline is designed for minimum latency at every stage. Market data is cached so detection never waits on network round-trips. Workers wake up the instant a signal is pushed — not on a fixed polling schedule.

01
Market data fetch10s cycle

The market data service polls Polymarket for trade history and open positions for every followed trader on a fixed interval. Results are written to a shared cache atomically so readers never see a partial update.

02
Signal detection< 1ms dedup lookup

The signal engine reads the fresh cache and checks each trade against a persistent deduplication store. Only trades not previously seen are promoted to signals. Same-market trades within the same scan window are aggregated into one.

03
Restart protectionone-time on boot

On first startup, the signal engine marks all currently-visible trades as already-seen. This prevents re-copying existing open positions after a server restart or configuration change.

04
Fan-out to client queues< 1ms per push

Each aggregated signal is pushed to a dedicated per-client queue. Clients following the same trader each receive an independent copy. Queues are bounded to prevent unbounded memory growth.

05
Instant worker wake-up~0ms delivery

Each execution worker blocks on its queue with a short timeout. The moment a signal arrives, the worker wakes immediately — no polling loop, no waiting for the next cycle. Delivery lag is effectively zero.

06
Sizing and execution< 500ms order round-trip

The worker sizes the order using the client's configured mode (equal, dynamic, or custom), reserves the required balance, and places a limit or market order on Polymarket. Fill confirmations arrive via an authenticated real-time stream.

Signal aggregation

When a trader places multiple orders in the same market within one scan window, the signal engine collapses them into a single signal with a combined position size. This reduces redundant order placement and more accurately reflects the trader's intent rather than reacting to each individual fill separately.

Signal expiry

Every signal carries an expiry timestamp. If an execution worker is restarting or paused and cannot process a signal within the TTL window, the signal is discarded rather than executed late. This prevents stale orders entering the market at incorrect prices.

Security

Compliance by design

Security isn't a feature we added — it's the constraint every architectural decision was made under. Private keys are never stored raw, never logged, and never reachable across client boundaries at any layer of the stack.

RSA-OAEP transit encryption

Your trading wallet's private key is encrypted in the browser using RSA-OAEP (SHA-256) before it is ever sent over the network. The plaintext key never appears in an HTTP request body.

AES-256-GCM at rest

On the server, the key is decrypted from transit ciphertext and immediately re-encrypted with AES-256-GCM using a server-side secret before being stored. The plaintext key is never persisted to disk or written to any log.

Container credential isolation

Each execution worker reads its credentials from a mount scoped exclusively to that client. No container can access another client's credentials, even if compromised. Credential files are readable only by the process owner.

Read-only execution environment

All source code is mounted read-only into containers. An execution worker cannot modify strategies, shared libraries, or any other client's configuration. The only writable surface is the client's own isolated data directory.

Non-custodial architecture

CopyForge never holds your funds. WalletConnect authenticates your identity. Your trading wallet signs orders directly on-chain. We have no ability to move funds without your private key.

No key logging

Private keys are never written to stdout, stderr, application logs, or any persistent storage in plaintext. The only moment a key exists in plaintext is in-process memory during initial provisioning, after which it is immediately re-encrypted.

Key lifecycle

Your key is encrypted client-side before transmission, decrypted server-side only to immediately re-encrypt for storage, and then passed to your execution worker's isolated environment at startup. At no point in this chain does the plaintext key touch a log, a database column, or a file readable by another client's process.

Access boundaries

  • Each execution worker can only read its own credentials — isolation is enforced at the container mount level, not just application logic
  • Credential files are written with restrictive permissions — no world or group read access
  • All source code and shared libraries are read-only inside containers
  • The message queue holds only trade signals, never credentials or keys
  • Database stores only encrypted key material — the plaintext is never written to any persistent store

Tech Stack

Built on proven primitives

The stack is intentionally straightforward — each component was chosen because it's the established best-in-class tool for its role. Async Python for I/O-bound execution, Redis for sub-millisecond queuing, Docker for isolation. No custom protocols, no exotic runtimes.

Frontend

Next.js 15App Router, server components, API routes
React 19Concurrent features, client-side interactivity
Tailwind CSSUtility-first styling, dark design system
Framer MotionPage transitions, animated UI
Reown AppKitWalletConnect v2 — wallet auth and identity
StripeSubscription billing, webhooks, plan enforcement

Execution Engine

Python 3.12Fully async event loop, high concurrency I/O
py-clob-clientOfficial Polymarket order book SDK
Redis clientQueue consumption, dedup state, execution tracking
aiohttpAsync HTTP for market data polling
websocketsAuthenticated real-time fill stream

Infrastructure

DockerContainer isolation per client, immutable deployments
RedisSignal queues, deduplication sets, ephemeral state
PostgreSQLBot configuration, encrypted credentials, subscription state
systemdBoot-time autostart for the container stack

Polymarket APIs

CLOB APIOrder placement, cancellation, orderbook reads
Data APITrade history and open position data
User WebSocketReal-time fill and cancellation events
Gamma APIMarket metadata and condition resolution

Production Specs

Numbers that matter

These values reflect the running production configuration. They are chosen to provide safe, stable operation across all client accounts simultaneously while leaving headroom for burst activity.

Timing

10s

market data refresh

25s

cache validity window

300s

signal expiry TTL

24h

dedup window

~0ms

queue delivery lag

500ms

order round-trip

Rate limiting

CopyForge uses token bucket rate limiting at 75% of Polymarket's documented thresholds. This headroom absorbs bursts, accommodates adaptive backoff when the exchange signals congestion, and ensures per-client fairness across the shared execution pool.

Endpoint
Limit
Note
Order placement
45 req/s
75% of exchange limit
Order cancellation
37 req/s
75% of exchange limit
Orderbook reads
67 req/s
75% of exchange limit
Market data
112 req/s
75% of exchange limit
Per-client
10 req/s
fairness across clients

Order constraints

  • Minimum order: $1.00 USDC — enforced by Polymarket at the exchange level
  • Minimum position: 5 shares per order — enforced by Polymarket at the exchange level
  • Limit orders (default): resting orders at a configurable price relative to the best offer
  • Market orders: Fill-or-Kill execution for immediate fills at current market price
  • Balance reservation: capital is committed before placement to prevent over-allocation across concurrent signals

Position sizing modes

Equal

Total exposure divided evenly across all tracked traders. Rebalances automatically when the trader list changes.

Dynamic

Allocation weighted by each trader's trailing performance score. Higher-performing traders receive proportionally larger size.

Custom

Fixed USD size per trader, set at configuration time. Full manual control over per-trader risk.