Skip to content

Commit 60d8418

Browse files
authored
Merge pull request #42 from usherlabs/develop
Bump master for telemtry, in-TEE proto handling, etc.
2 parents 632d3da + d20daf8 commit 60d8418

201 files changed

Lines changed: 29442 additions & 1983 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.sample

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
# Required for CLI and Optional for the Broker Library
2+
# Required for CLI and broker
33

44
CEX_BROKER_BYBIT_API_KEY=***********************
55
CEX_BROKER_BINANCE_API_KEY=****************************************
@@ -8,12 +8,39 @@ CEX_BROKER_BINANCE_API_SECRET=**************************************************
88
LOG_LEVEL=debug
99
CEX_BROKER_SANDBOX_MODE=true
1010

11-
# OpenTelemetry Configuration
12-
# Use OTEL_EXPORTER_OTLP_ENDPOINT for the full OTLP endpoint (preferred)
11+
# OpenTelemetry (optional)
1312
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
1413
OTEL_SERVICE_NAME=cex-broker
1514

1615
# Or use legacy host/port configuration
1716
# CEX_BROKER_OTEL_HOST=otel-collector
1817
# CEX_BROKER_OTEL_PORT=4318
19-
# CEX_BROKER_OTEL_PROTOCOL=http
18+
# CEX_BROKER_OTEL_PROTOCOL=http
19+
20+
# Travel-rule deposit auto-clear reconciler (only used when a policy has
21+
# travelRule.rule[].deposits.enabled). One RPC URL per Binance network code
22+
# (suffix must match the deposit's `network`, e.g. ARBITRUM), used to prove a
23+
# frozen deposit's on-chain sender before auto-submitting its questionnaire. A
24+
# frozen deposit on a network with no RPC configured is left frozen (fail-closed).
25+
# These are intentionally NOT CEX_BROKER_-prefixed so the credential scan skips them.
26+
# TRAVEL_RULE_RPC_URL_ARBITRUM=https://arb1.arbitrum.io/rpc
27+
# Optional overrides (defaults shown):
28+
# TRAVEL_RULE_DEPOSIT_POLL_ACTIVE_SECS=60
29+
# TRAVEL_RULE_DEPOSIT_POLL_IDLE_SECS=600
30+
# TRAVEL_RULE_QUESTIONNAIRE_COUNTRY=AU
31+
32+
# ClickHouse research / market data archive (optional)
33+
# Broker → forwarder
34+
CEX_BROKER_ARCHIVE_ENABLED=false
35+
CEX_BROKER_ARCHIVE_FORWARDER_URL=http://localhost:8090/archive
36+
# When enabled, this must point to persistent writable storage. A path only in
37+
# the container filesystem is not durable across container replacement.
38+
CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH=./archive-loss.jsonl
39+
CEX_BROKER_DEPLOYMENT_ID=local-dev
40+
# OHLCV collector: required JSON array config and reconnect bootstrap coverage.
41+
# 1000 one-minute bars cover roughly 16 hours on every (re)subscription.
42+
CEX_BROKER_OHLCV_ARCHIVE_BOOTSTRAP_LIMIT=1000
43+
CEX_BROKER_OHLCV_COLLECTOR_CONFIG=./ohlcv-subscriptions.json
44+
# ClickHouse (forwarder, candle-viewer, Python)
45+
CLICKHOUSE_HOST=localhost
46+
CLICKHOUSE_PORT=8123

.github/workflows/ci.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ name: CI
44

55
on:
66
push:
7-
branches: [master]
7+
branches: [master, develop]
88
pull_request:
9-
branches: [master]
9+
branches: [master, develop]
1010

1111
jobs:
1212
test:
@@ -31,4 +31,10 @@ jobs:
3131
run: bunx @biomejs/biome lint .
3232

3333
- name: Run tests
34-
run: bun test
34+
run: bun test
35+
36+
# Gate PRs on the same strict build the publish workflow runs on tag push
37+
# (dts-bundle-generator + strict tsc). Without this, develop can merge code
38+
# that only fails at publish time, blocking releases.
39+
- name: Build project
40+
run: bun run build
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Publishes the archive-forwarder image (services/archive-forwarder).
2+
# The forwarder is repo-runtime only — it is NOT part of the npm package
3+
# (files: ["dist"]), so it ships as its own container image.
4+
#
5+
# Triggers: semver tags (alongside the broker publish) and manual dispatch
6+
# (to publish from a branch without cutting an npm release).
7+
8+
name: Publish Archive Forwarder
9+
10+
on:
11+
push:
12+
tags:
13+
- 'v[0-9]*.[0-9]*.[0-9]*'
14+
- 'v[0-9]*.[0-9]*.[0-9]*-*'
15+
workflow_dispatch:
16+
17+
env:
18+
IMAGE_NAME: ghcr.io/usherlabs/cex-broker-archive-forwarder
19+
20+
permissions:
21+
contents: read
22+
packages: write
23+
24+
jobs:
25+
publish-docker:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout repo
29+
uses: actions/checkout@v4
30+
31+
- name: Set up Docker Buildx
32+
uses: docker/setup-buildx-action@v3
33+
34+
- name: Log in to GitHub Container Registry
35+
uses: docker/login-action@v3
36+
with:
37+
registry: ghcr.io
38+
username: ${{ github.actor }}
39+
password: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Extract metadata
42+
id: meta
43+
uses: docker/metadata-action@v5
44+
with:
45+
images: ${{ env.IMAGE_NAME }}
46+
tags: |
47+
type=match,pattern=v(\d+\.\d+\.\d+.*),group=1
48+
type=sha
49+
type=raw,value=latest,enable={{is_default_branch}}
50+
51+
- name: Build and push Docker image
52+
uses: docker/build-push-action@v6
53+
with:
54+
context: .
55+
file: services/archive-forwarder/Dockerfile
56+
push: true
57+
tags: ${{ steps.meta.outputs.tags }}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Publishes the OHLCV collector image (services/ohlcv-collector).
2+
# The collector is repo-runtime only — it is NOT part of the npm package
3+
# (files: ["dist"]), so it ships as its own container image.
4+
#
5+
# Triggers: semver tags (alongside the broker publish) and manual dispatch
6+
# (to publish from a branch without cutting an npm release).
7+
8+
name: Publish OHLCV Collector
9+
10+
on:
11+
push:
12+
tags:
13+
- 'v[0-9]*.[0-9]*.[0-9]*'
14+
- 'v[0-9]*.[0-9]*.[0-9]*-*'
15+
workflow_dispatch:
16+
17+
env:
18+
IMAGE_NAME: ghcr.io/usherlabs/cex-broker-ohlcv-collector
19+
20+
permissions:
21+
contents: read
22+
packages: write
23+
24+
jobs:
25+
publish-ohlcv-collector:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout repo
29+
uses: actions/checkout@v4
30+
31+
- name: Set up Docker Buildx
32+
uses: docker/setup-buildx-action@v3
33+
34+
- name: Log in to GitHub Container Registry
35+
uses: docker/login-action@v3
36+
with:
37+
registry: ghcr.io
38+
username: ${{ github.actor }}
39+
password: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Extract metadata
42+
id: meta
43+
uses: docker/metadata-action@v5
44+
with:
45+
images: ${{ env.IMAGE_NAME }}
46+
tags: |
47+
type=match,pattern=v(\d+\.\d+\.\d+.*),group=1
48+
type=sha
49+
type=raw,value=latest,enable={{is_default_branch}}
50+
51+
- name: Build and push Docker image
52+
uses: docker/build-push-action@v6
53+
with:
54+
context: .
55+
file: services/ohlcv-collector/Dockerfile
56+
push: true
57+
tags: ${{ steps.meta.outputs.tags }}

.github/workflows/publish.yml

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
tags:
88
- 'v[0-9]*.[0-9]*.[0-9]*' # Matches semver tags in the format of v1.2.3
99
- 'v[0-9]*.[0-9]*.[0-9]*-*' # Matches semver tags in the format of v1.2.3-beta
10+
# Recovery path: republish from a branch when a tag's run failed for
11+
# workflow-only reasons (a rerun executes the workflow file at the tag's
12+
# commit, so a fixed workflow can never rerun under the original tag).
13+
workflow_dispatch:
1014

1115
env:
1216
IMAGE_NAME: ghcr.io/usherlabs/cex-broker
@@ -32,12 +36,17 @@ jobs:
3236
- name: Setup Node.js for npm publishing
3337
uses: actions/setup-node@v4
3438
with:
35-
node-version: 20
39+
node-version: 24
3640
check-latest: true
3741
registry-url: "https://registry.npmjs.org"
3842

39-
- name: Ensure latest npm
40-
run: npm install -g npm@latest
43+
# This package publishes via npm trusted publishing (OIDC, id-token
44+
# permission) — no NPM_TOKEN secret exists. OIDC needs npm >= 11.5.1,
45+
# newer than any runner-bundled npm; without it the placeholder token is
46+
# sent and the registry PUT 404s. Pinned to a major because npm@latest
47+
# broke every tag run when npm 12 dropped the then-pinned Node 20.
48+
- name: Install npm with trusted-publishing support
49+
run: npm install -g npm@12
4150

4251
- name: Install dependencies
4352
run: bun install
@@ -74,20 +83,19 @@ jobs:
7483
username: ${{ github.actor }}
7584
password: ${{ secrets.GITHUB_TOKEN }}
7685

77-
- name: Extract metadata
78-
id: meta
79-
uses: docker/metadata-action@v5
80-
with:
81-
images: ${{ env.IMAGE_NAME }}
82-
tags: |
83-
type=match,pattern=v(\d+\.\d+\.\d+.*),group=1
84-
86+
# The release convention tags the version-bump commit, so package.json
87+
# is the version authority on both tag pushes and dispatch republishes
88+
# (a git-tag-derived name is empty on workflow_dispatch).
89+
- name: Read package version
90+
id: version
91+
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
92+
8593
- name: Build and push Docker image
8694
uses: docker/build-push-action@v6
8795
with:
8896
context: .
8997
file: Dockerfile
9098
push: true
9199
tags: |
92-
${{ env.IMAGE_NAME }}:${{ fromJSON(steps.meta.outputs.json).tag-names[0] }}
100+
${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
93101
${{ env.IMAGE_NAME }}:latest

.gitignore

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
2727
.cache
2828
*.tsbuildinfo
2929

30+
# research outputs
31+
research/python/examples/output/
32+
research/output/
33+
__pycache__
34+
*.egg-info
35+
3036
# IntelliJ based IDEs
3137
.idea
3238

@@ -36,5 +42,10 @@ build
3642
/proto/**
3743
src/proto/*.ts
3844
src/proto/**/*.ts
45+
!src/proto/node.descriptor.ts
46+
47+
src/assets/proto/**
3948

40-
src/assets/proto/**
49+
# Worktrees, Agents
50+
.codex
51+
.emdash

AGENTS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## Agent Orchestrator (ao) Session
2+
3+
You are running inside an Agent Orchestrator managed workspace.
4+
5+
## Source layout (cex-broker)
6+
7+
- `src/server.ts` — gRPC registration and handler wiring only; do not add domain logic here.
8+
- `src/handlers/` — RPC dispatch (`execute-action/`, `subscribe/`).
9+
- `src/helpers/` — domain and shared utilities (`shared/`, `grpc/`, `order-book.ts`, etc.).
10+
- Dependency direction: `server``handlers``helpers`. Helpers must not import from `server` or `handlers`.
11+
- Import concrete helper modules (e.g. `helpers/deposit`); avoid growing `helpers/index.ts` with server utilities.
12+
Session metadata is updated automatically via shell wrappers.
13+
14+
If automatic updates fail, you can manually update metadata:
15+
```bash
16+
~/.ao/bin/ao-metadata-helper.sh # sourced automatically
17+
# Then call: update_ao_metadata <key> <value>
18+
```

Dockerfile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ RUN apt-get update -y \
66
&& apt-get install -y --no-install-recommends ca-certificates curl \
77
&& rm -rf /var/lib/apt/lists/*
88

9-
RUN bun install --global @usherlabs/cex-broker@0.2.6
9+
COPY package.json bun.lock ./
10+
COPY patches ./patches
11+
RUN bun install --frozen-lockfile
1012

11-
CMD ["cex-broker"]
13+
COPY build.ts proto-gen.sh tsconfig.json ./
14+
COPY scripts ./scripts
15+
COPY src ./src
16+
RUN bun run build
17+
18+
CMD ["bun", "./dist/commands/cli.js"]

POLICY.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,17 @@ Accepted values:
110110
- **A network/chain identifier** — e.g. `"ARBITRUM"`, `"BEP20"`, `"ETH"`, `"SOL"`. The value must match what the exchange uses for that chain.
111111
- **`"*"`** — wildcard; matches any network.
112112

113-
Even if the policy allows a network, the selected exchange must also support that network for the currency or the request will still fail at execution time.
113+
The broker normalizes common operator aliases before matching policy:
114+
115+
| Operator alias | Broker network id |
116+
|----------------|-------------------|
117+
| `ARB`, `ARBITRUM` | `ARBITRUM` |
118+
| `ETH`, `ERC20`, `ETHEREUM` | `ETHEREUM` |
119+
| `BNB`, `BSC`, `BEP20` | `BNB` |
120+
121+
Even if the policy allows a normalized network, the selected exchange must also
122+
support that network for the currency or the request will still fail at
123+
execution time.
114124

115125
---
116126

@@ -191,6 +201,46 @@ Common rejection reasons:
191201
- address not whitelisted
192202
- token not in `coins` for the matched rule
193203

204+
### Narrow Binance/MEXC USDC BEP20 corridor example
205+
206+
Use a dedicated policy for treasury corridors instead of relying on broad
207+
exchange/network rules. The example
208+
`policy/policy.binance-mexc-usdc-bep20.example.json` permits only USDC over the
209+
normalized `BNB` network family (`BNB`, `BSC`, or `BEP20`) between Binance and
210+
MEXC:
211+
212+
```json
213+
{
214+
"withdraw": {
215+
"rule": [
216+
{
217+
"exchange": "BINANCE",
218+
"network": "BEP20",
219+
"coins": ["USDC"],
220+
"whitelist": ["0x1111111111111111111111111111111111111111"]
221+
},
222+
{
223+
"exchange": "MEXC",
224+
"network": "BEP20",
225+
"coins": ["USDC"],
226+
"whitelist": ["0x2222222222222222222222222222222222222222"]
227+
}
228+
]
229+
},
230+
"deposit": {
231+
"rule": [
232+
{ "exchange": "MEXC", "network": "BEP20", "coins": ["USDC"] },
233+
{ "exchange": "BINANCE", "network": "BEP20", "coins": ["USDC"] }
234+
]
235+
}
236+
}
237+
```
238+
239+
This policy does not authorize volatile inventory transfer. If a treasury
240+
ceremony chooses remote volatile acquisition, that acquired-asset transfer must
241+
be explicitly requested, live-discovered, policy-approved, cost-gated, and
242+
attested by the caller.
243+
194244
---
195245

196246
## Order policy (`order.rule`)

0 commit comments

Comments
 (0)