Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bun-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.3.14
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
# — OR inline the PEM directly —
# KALSHI_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...\n-----END PRIVATE KEY-----"

# Authorized partner execution resolves credentials by out, then partner, then
# the global KALSHI_ fallback. Example for out-SPORTS-1:
# KALSHI_SPORTS_1_API_KEY_ID=your_out_scoped_key_id
# KALSHI_SPORTS_1_PRIVATE_KEY_PATH=/path/to/out-scoped-private-key.pem
# Partner-wide fallback: KALSHI_SPORTS_API_KEY_ID / KALSHI_SPORTS_PRIVATE_KEY_PATH

# The Odds API key for Pinnacle consensus feed
# Get from: https://the-odds-api.com/
# ODDS_API_KEY=your_odds_api_key_here
Expand All @@ -35,6 +41,10 @@
# Prod additionally requires:
# KALSHI_PROD_ARMED=1

# Independent fail-closed breaker for POST /api/trading/order live execution.
# Leave unset for dry-run-only HQ behavior.
# KALSHI_AUTHORIZED_EXECUTION_ENABLED=1

# === RESEARCH PIPELINE (optional) ===

# Popularity gate overrides
Expand Down
19 changes: 19 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Outcome

<!-- State the user/operator-visible result first. -->

## What changed

<!-- Describe the owned source, schema, configuration, and documentation changes. -->

## Safety and compatibility

<!-- Name fail-closed behavior, migrations, flags, provider boundaries, and runtime compatibility. -->

## Validation

<!-- List focused tests and `bun run bun:ci`. Hosted checks are supporting evidence only. -->

## Follow-up

<!-- Record intentionally deferred work. Use "None" when the increment is complete. -->
11 changes: 5 additions & 6 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# Same gate as tools/pre-commit.sh / bun run check
name: check
# Manual diagnostic only. Local `bun run bun:ci` is merge authority because
# hosted runners are billing-blocked for this repository.
name: check (manual diagnostic)

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

jobs:
check:
Expand All @@ -13,7 +12,7 @@ jobs:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: "canary"
bun-version: "1.3.14"
- name: Install
run: bun install --frozen-lockfile
- name: Check
Expand Down
36 changes: 36 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Kalshi-bot Local Authority

This standalone repository inherits `/Users/nolarose/Projects/AGENTS.md` and
the global DX context. This file narrows their application; it does not weaken
runtime safety.

## Commands

- Bun 1.3.14 stable is the supported local and production baseline.
- Use `bun run bun:ci` as local merge proof. `bun run check` is the same owned
gate beneath it.
- GitHub Actions is a manual diagnostic only while hosted runners are
billing-blocked. A missing hosted check is not merge authority.
- Use focused `bun test <paths>` while developing. Do not translate Node/Jest
worker flags into Bun flags.

## Authorized execution

- Live HTTP orders must enter through `handleTradingOrder` and
`executeKalshiLiveOrder`, then reach the provider only through
`executeAuthorizedBet`.
- Never bypass compliance, an active SQLite authorization grant, policy-hash
verification, executable-book freshness, balance/liquidity caps, exposure
reservation, provider idempotency, or the global risk breaker.
- `KALSHI_AUTHORIZED_EXECUTION_ENABLED=1` opens only the partner-route breaker.
Production additionally requires `KALSHI_ENV=prod` and
`KALSHI_PROD_ARMED=1`.
- Fantasy402 live execution remains unavailable until its provider-side
idempotency contract is proven.

## Repository hygiene

- Preserve unrelated dirty and untracked files. Stage with explicit paths.
- Runtime policy, agent instructions, skills, hooks, and documentation cannot
enable live execution; only the runtime gates and verified database state can.
- See `docs/AUTHORIZED_EXECUTION.md` for the operational work card.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

Standalone Bun project for discovering and ranking public [Kalshi](https://kalshi.com) trading bots on GitHub.

**Zero runtime npm dependencies** — Bun + authenticated [`gh`](https://cli.github.com/) CLI only.
**Bun-native runtime** with a deliberately small dependency surface.

## Prerequisites

- [Bun](https://bun.sh) >= 1.3.13 ([`URLPattern`](https://bun.com/blog/bun-v1.3.4#urlpattern-api), [`Bun.cron`](https://bun.com/docs/runtime/cron), [SHA3-256](https://bun.com/blog/bun-v1.3.13#sha3-support-in-webcrypto-and-node-crypto))
- [Bun](https://bun.sh) >= 1.3.14 ([`URLPattern`](https://bun.com/blog/bun-v1.3.4#urlpattern-api), [`Bun.cron`](https://bun.com/docs/runtime/cron), [SHA3-256](https://bun.com/blog/bun-v1.3.13#sha3-support-in-webcrypto-and-node-crypto))
- GitHub CLI on PATH (`gh auth login`)
- No `bun install` required — zero npm deps; see [`docs/BUN_NATIVE.md` — Package manager](docs/BUN_NATIVE.md#package-manager)
- `bun install --frozen-lockfile`
- Optional secrets via [Proton Pass CLI](https://protonpass.github.io/pass-cli/) — see [`docs/PROTONPASS.md`](docs/PROTONPASS.md)
- Authorized execution operators: [`docs/AUTHORIZED_EXECUTION.md`](docs/AUTHORIZED_EXECUTION.md)

## Quick start

Expand All @@ -23,15 +24,15 @@ bun run agent status # latest run from cache.db
bun run agent patterns # pattern extract from cached run
bun run agent blueprint # architecture blueprint from cache
bun run report:term # ANSI latest.md in terminal
bun test && bun run typecheck # posttest restores committed artifacts from fixtures
bun run bun:ci # guard + typecheck + tests + artifact restore
```

### Commit flow

Tests can overwrite `latest.md` or audit JSONL — **`posttest` restores from fixtures** automatically. Before committing:

```bash
bun run check # typecheck + test + artifact restore
bun run bun:ci # local merge authority
bun run hooks:install # once: install pre-commit gate
git add … && git commit # pre-commit runs check + deletion guard
```
Expand Down Expand Up @@ -103,7 +104,7 @@ Niche dimensions (`sports-nba`, `tracking`, …) may discover candidates but pro
| Restore artifacts | `bun run artifacts:restore` — fixtures → reports + audit JSONL |
| Pre-commit gate | `bun run hooks:install` then `git commit` runs `bun run check` |
| Types | `bun run typecheck` |
| Full check | `bun run check` — typecheck + test |
| Full check | `bun run bun:ci` — guard + typecheck + test; local merge authority |

## Cache, diff, and artifacts

Expand Down
3 changes: 2 additions & 1 deletion bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ depth = 3
# Override any key at runtime via env vars:
# KALSHI__REGULATORY__DATABASE_PATH=/tmp/test.db bun run script.ts
#
# Native API reference:
# Native API reference. `src/partner/toml-stringify.ts` owns the stable-runtime
# fallback until the selected stable Bun ships stringify:
# Bun.TOML.parse(string) -> object
# Bun.TOML.stringify(object) -> string
# @see https://bun.com/docs/api/toml
Expand Down
96 changes: 96 additions & 0 deletions docs/AUTHORIZED_EXECUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Authorized Partner Execution

Status: implemented, default off. This is the operational work card for the
authorization, Telegram approval, exposure reservation, Kalshi mapping, and
live HTTP orchestration layers.

## Authority boundary

Live provider placement follows one path:

```text
HTTP compliance
→ canonical partnerCode / outId / skin request
→ active SQLite authorization + immutable policy hash
→ fresh executable Kalshi book + live portfolio balance
→ integer stake caps + transactional exposure reservation
→ idempotent Kalshi V2 placement
→ confirmed, rejected, or unknown reservation + durable receipt
```

Skills, agent instructions, documentation, hooks, CI, and dashboard controls do
not grant trading permission. They can only inspect, test, or document this
path. Runtime permission comes from verified database state and the explicit
environment gates below.

## Runtime gates

| Gate | Expected behavior |
|------|-------------------|
| `KALSHI_AUTHORIZED_EXECUTION_ENABLED=1` | Opens the partner HTTP execution breaker; unset is dry-run/fail-closed |
| `KALSHI_ENV=demo` | Default provider host; does not require production arming |
| `KALSHI_ENV=prod` | Selects the production provider host |
| `KALSHI_PROD_ARMED=1` | Required in addition to `KALSHI_ENV=prod` |
| active authorization grant | Must match partner, out, skin, provider, currency, scope, validity, and policy hash |
| risk health | Must remain healthy before and during snapshot evaluation |

`KALSHI_ALPHA_LIVE` belongs to alpha programs and does not enable this route.

## Request contract

Live `POST /api/trading/order` requests require:

- `partnerCode`, canonical `outId`, active `skin`, `ticker`, and `outcome`
- integer `stakeMinorUnits` and `priceCents`
- an explicit, stable `Idempotency-Key`
- compliance fields and middleware context: state, node, sport, market, wager,
and bet type

The route rejects post-only requests because the authorization snapshot binds
the order to immediately executable top-of-book liquidity.

## Credentials and provider state

Kalshi credentials resolve in this order:

1. out: `KALSHI_SPORTS_1_*`
2. partner: `KALSHI_SPORTS_*`
3. global fallback: `KALSHI_*`

The client cache fingerprints credential inputs and rebuilds automatically
after key rotation. Kalshi uses signed RSA requests rather than a refresh-token
flow; the live `/portfolio/balance` call proves the current credentials and
supplies available balance to the gate.

## Expected fail-closed outcomes

- Missing/mismatched partner, out, skin, provider, currency, or grant: denied.
- Stale/missing/crossed book, quote mismatch, unavailable balance, or unhealthy
risk state: denied before reservation or provider placement.
- Known provider rejection: reservation failed and exposure released.
- Ambiguous provider outcome: reservation remains unknown and exposure remains
held for reconciliation.
- Fantasy402: HTTP 501 with no provider call.

Credit lines and dedicated partner wallets are not modeled in the current
registry, so the gate does not invent either. Available capacity is the live
Kalshi balance constrained by authorization, skin, daily, exposure, max-win,
and executable-liquidity limits.

## Operator proof

```bash
bun test tests/partner/authorization tests/partner/execution tests/research/trading-order.test.ts
bun run bun:ci
```

`Bun.TOML.stringify` is optional on the stable runtime: the governed
`src/partner/toml-stringify.ts` boundary uses the native API when present and a
tested compatibility serializer otherwise.

## Remaining work

- Build the Kalshi unknown-outcome reconciliation poller.
- Keep Fantasy402 unwired until provider-side idempotency is proven.
- Add credit-line or dedicated-wallet accounting only when an owned domain
contract and ledger source exist.
20 changes: 17 additions & 3 deletions docs/ENV_NAMING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Every env var MUST have a service prefix. Examples:

| Pattern | Example |
|---------|---------|
| `KALSHI_*` | `KALSHI_API_KEY_ID`, `KALSHI_PROD_ARMED` |
| `KALSHI_*` | `KALSHI_API_KEY_ID`, `KALSHI_PROD_ARMED`, `KALSHI_AUTHORIZED_EXECUTION_ENABLED` |
| `TELEGRAM_*` | `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALERT_CHAT_ID` |
| `TENNIS_*` | `TENNIS_LIVE_INTERVAL_MS`, `TENNIS_WS_RECORDER_CRON_SCHEDULE` |
| `RESEARCH_*` | `RESEARCH_DIMENSION`, `RESEARCH_CRON_SCHEDULE` |
Expand All @@ -32,10 +32,10 @@ Every env var MUST have a service prefix. Examples:
| `_SECONDS` | Duration in seconds | `TENNIS_WS_RECORDER_WS_SECONDS` |
| `_SCHEDULE` | Cron expression | `RESEARCH_CRON_SCHEDULE` |
| `_TITLE` | Cron job title | `RESEARCH_CRON_TITLE` |
| `_LIVE` | Boolean toggle (live mode) | `ALPHA_LIVE` |
| `_LIVE` | Boolean toggle (live mode) | `KALSHI_ALPHA_LIVE` |
| `_ARMED` | Safety gate (must be "1") | `KALSHI_PROD_ARMED` |
| `_WAIT` | Blocking flag | `GITHUB_RATE_LIMIT_WAIT` |
| `_ENABLED` | Boolean toggle | `RESEARCH_EXPORT_AUDIT` |
| `_ENABLED` | Boolean toggle | `KALSHI_AUTHORIZED_EXECUTION_ENABLED` |

## Cron pairs

Expand Down Expand Up @@ -67,6 +67,20 @@ When renaming, add a backward-compat read wrapper:
const hubUrl = Bun.env.OPS_DASHBOARD_URL ?? Bun.env.SERVE_URL;
```

## Live execution gates

The flags are independent and conjunctive; none is an alias for another:

| Variable | Owns |
|----------|------|
| `KALSHI_ENV=prod` | Selects the production Kalshi API host |
| `KALSHI_PROD_ARMED=1` | Permits construction of a production Kalshi client |
| `KALSHI_AUTHORIZED_EXECUTION_ENABLED=1` | Opens the authorized partner HTTP execution breaker |
| `KALSHI_ALPHA_LIVE=1` | Alpha-program execution only; it does not open the partner route |

All authorization, compliance, balance, liquidity, session, exposure, and risk
checks still run after the environment gates. Missing or false flags fail closed.

## Rename log

| Old name | New name | Date |
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "kalshi-bot-research",
"version": "0.2.0",
"packageManager": "bun@1.4.0-canary.1",
"packageManager": "bun@1.3.14",
"private": true,
"description": "Discover and rank public Kalshi trading bots on GitHub (Bun + gh CLI)",
"type": "module",
Expand Down Expand Up @@ -33,6 +33,7 @@
"artifacts:restore": "bun tools/restore-committed-artifacts.ts",
"guard": "bun scripts/audit-bun-native.ts",
"check": "bun run guard && bun run typecheck && bun run test",
"bun:ci": "bun run check",
"rate-limit:status": "bun tools/github-rate-budget.ts",
"miss-taxonomy:status": "bun tools/miss-taxonomy-status.ts",
"hooks:install": "cp tools/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit",
Expand Down Expand Up @@ -157,7 +158,7 @@
"test:liquidity": "bun test --parallel tests/institutions/match-liquidity.test.ts tests/institutions/match-liquidity-ground.test.ts tests/institutions/match-liquidity-backfill.test.ts tests/institutions/match-liquidity-pipeline.test.ts"
},
"engines": {
"bun": ">=1.4.0-canary.1"
"bun": ">=1.3.14"
},
"dependencies": {
"drizzle-orm": "^0.45.2",
Expand Down
Loading