keys.json fields¶
{
"version": 1, // fixed at 1 for now; future schema changes bump this
"keys": [
{ /* KeyRecord */ }
]
}
KeyRecord fields¶
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Human-readable name, unique within keys.json; shown as key_id in audit |
hash |
string | ✓ | SHA-256 hex of the plaintext key |
scopes |
["qot:read", "acc:read", ...] |
✓ | What this key can do |
limits |
object | — (empty = unlimited) | Limits, see table below |
allowed_machines |
[fp, ...] / null |
— | null = no binding; [] = frozen; [fp1,fp2] = whitelist |
created_at |
RFC3339 timestamp | ✓ | Creation time |
expires_at |
RFC3339 / null | — | null = never expires |
note |
string | — | Free-form note |
limits fields¶
| Field | Type | Description |
|---|---|---|
allowed_markets |
["HK","US","CN","HKCC",...] / null |
Market whitelist |
allowed_symbols |
["HK.00700","US.AAPL"] / null |
Symbol whitelist |
allowed_trd_sides |
["BUY","SELL","SELL_SHORT","BUY_BACK"] / null |
Direction whitelist |
max_order_value |
float / null | Per-order cap (qty × price, in local currency) |
max_daily_value |
float / null | Daily rolling cap (resets at UTC midnight) |
max_orders_per_minute |
u32 / null | Rate limit (60-second sliding window) |
hours_window |
"HH:MM-HH:MM" / null | Time window (local timezone; cross-midnight written as 22:00-04:00) |
null / omitted = unlimited.
Scope strings¶
| Scope | What it unlocks |
|---|---|
qot:read |
Quote read + subscribe |
acc:read |
Account read-only |
trade:simulate |
Simulated trading writes |
trade:real |
Real trading writes |
trade:unlock |
Used by the MCP futu_unlock_trade tool (v1.4+) |
Examples¶
Minimal read-only¶
{
"id": "research",
"hash": "a1b2...",
"scopes": ["qot:read"],
"created_at": "2026-04-15T10:00:00Z"
}
Tightly-limited simulation bot¶
{
"id": "sim-bot",
"hash": "c4d5...",
"scopes": ["qot:read", "acc:read", "trade:simulate"],
"limits": {
"allowed_markets": ["HK"],
"allowed_symbols": ["HK.00700", "HK.09988"],
"max_order_value": 100000,
"max_daily_value": 500000,
"max_orders_per_minute": 3,
"hours_window": "09:30-16:00",
"allowed_trd_sides": ["SELL"]
},
"allowed_machines": ["fp_bot_host_abc123"],
"created_at": "2026-04-15T10:00:00Z",
"expires_at": "2026-05-15T10:00:00Z",
"note": "simulated take-profit sell bot"
}
Operations¶
All mutations go through futucli — do not edit the file by hand
(atomic writes avoid concurrent corruption; hand-writing SHA-256 hashes
is error-prone):
futucli gen-key ... # add
futucli list-keys # list
futucli revoke-key <id> # remove
futucli bind-key <id> ... # edit allowed_machines
After editing, kill -HUP the gateway / MCP to hot-reload.