unrip/README.md
philipp 41b9ec680b
Some checks failed
deploy / deploy (push) Failing after 1m35s
Implement funded NEAR Intents trade loop
Proof: first non-mocked tradeable loop for one pair using funded NEAR Intents inventory, Kafka, and PostgreSQL.

Assumptions: solver-side execution is performed by signed token_diff quote responses over the Solver Relay; EURe is treated as 1:1 with EUR; k3s runtime uses unrip-dev.near as the named signer account.

Still fake: signer key is not yet registered on intents.near, strategy and executor remain disarmed by default, and no live mainnet quote response has been submitted from this repo yet.
2026-04-02 10:01:15 +02:00

316 lines
11 KiB
Markdown

# unrip project
This repository contains the unrip trading-system code and its project-specific deployment assets.
## Contents
- `src/` — application code
- `package.json` / `package-lock.json` — Node package manifest
- `Dockerfile` / `.dockerignore` — app container build
- `.env.example` — local app runtime example
- `compose.yml` — local development stack
- `deploy/k8s/base/` — project-specific Kubernetes manifests
- `deploy/redpanda/rpk-topics.txt` — project topic reference
- `docs/` — project-specific design and contract docs
## Local development
```bash
npm install
cp .env.example .env
# edit .env
docker compose up -d --build
```
Useful commands:
```bash
docker compose ps
docker compose logs -f
docker compose logs -f \
near-intents-ingest market-reference-ingest liquidity-manager \
inventory-sync history-writer strategy-engine trade-executor
npm run near-intents:ingest
npm run market-reference:ingest
npm run liquidity:manager
npm run inventory:sync
npm run history:writer
npm run strategy:engine
npm run trade:executor
```
## App image
The app image is now built from this directory.
Examples:
```bash
docker build -t unrip:dev .
```
## Kubernetes manifests
Project manifests live under:
- `deploy/k8s/base/`
The shared cluster/platform resources live in the separate infra repository.
## Deployment
This repo is the app-side deployment repo. The shared Hetzner/k3s bootstrap,
Forgejo runner, registry, and other platform services live in the separate
platform repo.
See `docs/deployment.md` for the full operator path.
See `docs/operator-runbook.md` for the live turn control and arming sequence.
### One-time app bootstrap
Bootstrap the app namespace, secrets, and Forgejo repo settings from this repo:
```bash
bash scripts/deploy/bootstrap.sh
```
That bootstrap also refreshes the local `forgejo` remote URL for HTTPS pushes
when it has enough auth material to do so.
By default, the script uses the adjacent platform checkout at `../unrip3` for:
- `kubeconfig.yaml`
- `kubeconfig.incluster.yaml`
- registry credentials
- the `NEAR_INTENTS_API_KEY` fallback from `../unrip3/.env`
If you are not using that local split, provide the values yourself via env vars
such as `KUBECONFIG_PATH`, `CI_KUBECONFIG_PATH`, `REGISTRY_HOST`,
`REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, `NEAR_INTENTS_API_KEY`, and either
`FORGEJO_TOKEN` or `FORGEJO_ADMIN_USERNAME` / `FORGEJO_ADMIN_PASSWORD`.
### Routine deploy
After bootstrap, deployment is just a push to Forgejo `main`:
```bash
git push forgejo main
```
`.forgejo/workflows/deploy.yml` then:
- applies `deploy/k8s/base`
- builds the image from this repo root inside the cluster with Kaniko
- pushes it to the shared registry
- rolls the `unrip` deployments
- uses a fresh temporary runner workspace on each run, so reruns do not require
manual cleanup on the Forgejo runner
### Observe rollout
```bash
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip get deploy,pods,job
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/near-intents-ingest
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/market-reference-ingest
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/liquidity-manager
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/inventory-sync
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/history-writer
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/strategy-engine
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip rollout status deploy/trade-executor
```
### Auxiliary ops scripts
These scripts default to the same adjacent platform checkout as the deployment
bootstrap: `../unrip3/.state/hetzner/kubeconfig.yaml`. Override with
`KUBECONFIG_PATH`, `KUBECONFIG`, or `PLATFORM_REPO_DIR` if needed.
`scripts/ops/deployment_status.py`
- Shows current deployment readiness, pod uptime, restart counts, and mounted storage usage.
- Outputs JSON by default so it can be piped directly into `jq`.
- By default it shows the live deployment pods only.
- Use `--include-completed` to include completed Job pods.
- Use `--include-rootfs` if you also want a probe of `/` for pods without PVC-backed mounts.
- Use `--output table` for the human-readable table view.
```bash
python3 scripts/ops/deployment_status.py
python3 scripts/ops/deployment_status.py | jq '.pods[] | {name, uptime}'
python3 scripts/ops/deployment_status.py --include-completed
python3 scripts/ops/deployment_status.py --output table
```
`scripts/ops/redpanda_storage.py`
- Shows how much data Redpanda is currently storing for the unrip topics.
- Outputs JSON by default so it can be piped directly into `jq`.
- Includes per-topic local bytes, total bytes, segment counts, and the overall Redpanda data-path usage.
- Use `--all-topics` to inspect every visible topic, or `--topic` multiple times for a subset.
- Use `--output table` for the human-readable table view.
```bash
python3 scripts/ops/redpanda_storage.py
python3 scripts/ops/redpanda_storage.py | jq '.totals'
python3 scripts/ops/redpanda_storage.py --all-topics
python3 scripts/ops/redpanda_storage.py --topic raw.near_intents.quote --topic norm.swap_demand
python3 scripts/ops/redpanda_storage.py --output table
```
`scripts/ops/live_near_intents.py`
- Reads the raw NEAR quote stream entering Redpanda.
- Outputs clean JSON by default. Bounded reads return a JSON array that can be piped directly into `jq`.
- `--num N` means "consume N records starting from the chosen offset". With the default `--offset end`, that means "wait for N new records from now".
- Use `--last N` when you want the most recent retained N records.
- Use `--offset start` to read from the beginning of retained history.
- Use `--output text` for the human-readable stream view or `--output jsonl` for one JSON object per line.
- Use `--value-only` to emit only decoded record values in JSON mode.
- Use `--timeout` when you want the script to stop automatically.
```bash
python3 scripts/ops/live_near_intents.py --last 10
python3 scripts/ops/live_near_intents.py --last 10 | jq '.[].value.payload.message.quote_id'
python3 scripts/ops/live_near_intents.py --num 10 --offset start
python3 scripts/ops/live_near_intents.py --num 10 --timeout 30
python3 scripts/ops/live_near_intents.py --value-only --last 5
python3 scripts/ops/live_near_intents.py --output text
```
### Near Intents control API
`near-intents-ingest` exposes a small in-process control API on port `8081` by
default. It is meant for ad hoc inspection and runtime filter changes without a
redeploy.
Port-forward the deployment:
```bash
KUBECONFIG=../unrip3/.state/hetzner/kubeconfig.yaml kubectl -n unrip port-forward deploy/near-intents-ingest 8081:8081
```
Inspect current state, including the active pair filter and ingest counters:
```bash
curl -s http://127.0.0.1:8081/state
```
Set or disable the runtime pair filter:
```bash
curl -s -X PUT http://127.0.0.1:8081/pair-filter \
-H 'content-type: application/json' \
-d '{"pair":"nep141:btc.omft.near->nep141:eth.omft.near"}'
curl -s -X PUT http://127.0.0.1:8081/pair-filter \
-H 'content-type: application/json' \
-d '{"pair":null}'
```
Reset the runtime filter back to the configured env/file/default state:
```bash
curl -s -X POST http://127.0.0.1:8081/pair-filter/reset
```
## Agent workflow
This repo now carries a small tracked workflow layer for Codex or other agents.
It is meant to create pressure toward real product progress without introducing
heavy orchestration.
The key files are:
- `AGENTS.md` — hard rules for agent behavior in this repo
- `THESIS.md` — stable product intent and approval boundaries
- `PROOF.md` — the active implementation proof
- `IMPLEMENTATION.md` — the current implementation turn
- `research/ACTIVE.md` — the active research charter
- `BACKLOG.md` — parked ideas, bugs, and future turn candidates
- `ARCHIVE.md` — index of archived turns and planning events
- `workflow/REVIEW_PROMPT.md` — adversarial review prompt for a separate review-only run
Two important rules shape the workflow:
- quote collection and analytics are first-class from day one
- backlog items do not automatically become active implementation without an explicit turn-opening step
### Install the tracked git hook
Install the tracked hook path once per clone:
```bash
bash scripts/workflow/install_hooks.sh
```
That sets `core.hooksPath` to `.githooks`.
The commit hook rejects non-merge commits unless the commit message body
contains:
- `Proof: ...`
- `Assumptions: ...`
- `Still fake: ...`
### Workflow scripts
`scripts/workflow/add_backlog.py`
- Append a new idea, bug, research item, or ops task to `BACKLOG.md`.
- Prints the created stable backlog ID.
```bash
python3 scripts/workflow/add_backlog.py --lane implementation --summary "Durable sink for normalized events"
python3 scripts/workflow/add_backlog.py --lane research --summary "Test whether quote freshness predicts worse downstream execution"
python3 scripts/workflow/add_backlog.py --lane bug --summary "Replay output drops pair metadata"
```
`scripts/workflow/open_turn.py`
- Opens a new implementation or research turn.
- Pulls selected backlog items into the active turn and removes them from `BACKLOG.md`.
- Refuses to overwrite an already-open turn unless `--force` is passed.
- Use `--commit` if you want the planning change committed automatically.
```bash
python3 scripts/workflow/open_turn.py \
--lane implementation \
--title "bounded replay for durable quote history" \
--summary "Replay recent history for the configured pair from the durable store." \
--pick I002 \
--pick B001
```
`scripts/workflow/close_turn.py`
- Archives the current implementation or research turn into `archive/`.
- Resets the live turn file back to `idle`.
- Updates `ARCHIVE.md`.
- Use `--commit` if you want the archive change committed automatically.
```bash
python3 scripts/workflow/close_turn.py \
--lane implementation \
--status passed \
--summary "Durable history landed in cluster storage and replay works for recent windows."
```
Possible close statuses are:
- `passed`
- `failed`
- `paused`
- `abandoned`
`scripts/workflow/review_diff.sh`
- Builds a review bundle consisting of a git diff plus the adversarial review prompt.
- Intended for a separate review-only agent run.
```bash
bash scripts/workflow/review_diff.sh HEAD~1
bash scripts/workflow/review_diff.sh main...HEAD
```
### Current workflow state
At the moment, the seeded active implementation proof is in:
- `PROOF.md`
- `IMPLEMENTATION.md`
That proof is focused on the first real quote -> reference-price -> decision ->
execute loop for the live configured pair, with PostgreSQL as the first durable
audit and analytics store behind the Kafka backbone.