85 lines
2.5 KiB
Markdown
85 lines
2.5 KiB
Markdown
# Event contracts
|
|
|
|
## Envelope
|
|
All bus messages use this envelope:
|
|
|
|
```json
|
|
{
|
|
"event_id": "string",
|
|
"event_type": "string",
|
|
"venue": "string",
|
|
"source": "string|null",
|
|
"schema_version": 1,
|
|
"observed_at": "ISO-8601|null",
|
|
"ingested_at": "ISO-8601",
|
|
"payload": {},
|
|
"raw": {}
|
|
}
|
|
```
|
|
|
|
## Topics
|
|
Current canonical topic set:
|
|
- `raw.near_intents.quote`
|
|
- `norm.swap_demand`
|
|
- `cmd.execute_trade`
|
|
- `exec.trade_result`
|
|
|
|
In Kubernetes bootstrap, Redpanda topic creation is currently handled by the repo-managed bootstrap job applied with the manifest set.
|
|
|
|
## `raw.near_intents.quote`
|
|
- `event_type`: `near_intents_quote_raw`
|
|
- `payload.message`: original venue-native payload
|
|
- `raw`: original venue-native payload
|
|
|
|
## `norm.swap_demand`
|
|
- `event_type`: `swap_demand`
|
|
- payload:
|
|
- `quote_id`
|
|
- `asset_in`
|
|
- `asset_out`
|
|
- `amount_in`
|
|
- `amount_out`
|
|
- `ttl_ms`
|
|
|
|
## `cmd.execute_trade`
|
|
- `event_type`: `execute_trade`
|
|
- payload:
|
|
- `command_id`
|
|
- `idempotency_key`
|
|
- `execution_key`
|
|
- `quote_id`
|
|
- `asset_in`
|
|
- `asset_out`
|
|
- `amount_in`
|
|
- `amount_out`
|
|
- `reason`
|
|
|
|
## `exec.trade_result`
|
|
- `event_type`: `trade_result`
|
|
- payload:
|
|
- `command_id`
|
|
- `idempotency_key`
|
|
- `execution_key`
|
|
- `quote_id`
|
|
- `status`
|
|
- `result_code`
|
|
- `note`
|
|
|
|
## Executor idempotency model
|
|
- `command_id` is unique per trade command and currently deterministic as `cmd-${quote_id}`
|
|
- `idempotency_key` is stable for semantic duplicate detection and currently `${venue}:${quote_id}`
|
|
- `execution_key` is the stable partition key and currently `${venue}:${asset_in}->${asset_out}`
|
|
- executor persists command state on durable storage before publishing a result
|
|
- already-completed `command_id`s are skipped on replay or restart
|
|
- if a command is seen again after a persisted `processing` state, the executor emits a recovered result path instead of blindly duplicating work
|
|
|
|
## Deployment and persistence implications
|
|
These contracts are tied to deployment behavior:
|
|
- executor duplicate suppression depends on durable persistence at `EXECUTOR_STATE_DIR`
|
|
- local Compose mounts that path for development/runtime testing
|
|
- the Hetzner single-node k3s path mounts persistent storage for the executor at `/var/lib/unrip/executor-state`
|
|
- in the current single-node target, that persistence is node-backed and should be treated as required operational state
|
|
|
|
Operational consequence:
|
|
- deleting the executor PVC or losing the node without migration discards idempotency history
|
|
- that can allow already-seen commands to be treated as new after recovery
|