We give your phone‑native gateway three new powers, all composable with Steps 1–8:
- Adaptive per‑plugin polling with exponential backoff and gentle recovery
- Rate limits by route and role (policy file)
- Audit JSONL for auth decisions, policy hits/misses, and rate‑limit drops
Plus: tokens can now carry audiences and per‑plugin grants; optional Ed25519 (S2) token verification if libs are present. Still read‑only.
✅ Fresh artifacts
- solveforce_phone_nine.py — Download
SHA‑256:e53c75e862432a516928e5a4b6e4d429d2f3965041b1b018a2642d802c808ad4 - Rate‑policy example — Download
SHA‑256:4842faf8bc32982ebd8090d5153505a3c8bf3466ec4f0521ec1ef13731bd3ca8
Step Nine is drop‑in compatible with Step Eight. Keep your Step Eight policy file as‑is; the new rate‑policy is separate and optional.
What’s new (precise, composable)
1) Adaptive per‑plugin polling
- Base poll interval via
--poll(seconds). - Per‑plugin overrides:
--poll-plugin name:seconds(repeatable). - On error/invalid, interval backs off:
cur = min(cur * --poll-backoff-mult, --poll-backoff-max). - On success/valid, interval decays toward base:
cur = max(base, cur * --poll-recover-mult). - Guardrails:
--poll-min(seconds). - The
/readAPI still works on demand; adaptive polling runs in the background if enabled.
Example
python solveforce_phone_nine.py \
--poll 5 \
--poll-plugin battery:3 \
--poll-plugin net:10 \
--poll-backoff-mult 2.0 \
--poll-recover-mult 0.7 \
--poll-backoff-max 300 \
--poll-min 1
2) Rate‑limits by route and role
Define limits per route (read, history, events, metrics, introspect, admin, ui, health, default) and adjust per role. No DB; just a tiny policy engine and sliding‑window counters.
- Config file:
--rate-policy-file solveforce_rate_policy_example.json
{
"window_sec": 60,
"routes": {
"default": { "max": 120 },
"events": { "max": 12 },
"metrics": { "max": 60 },
"admin": { "max": 30 },
"read": { "max": 180 },
"introspect": { "max": 180 }
},
"roles": {
"admin": { "mult": 5 },
"metrics": { "mult": 2 },
"netops": { "max": 300 }
}
}
- The engine computes an effective ceiling for each request using the base route limit, then applying each role’s
maxormult(taking the highest ceiling). - 429s include
Retry-After, andsolveforce_rate_limited_totalincrements.
3) Audit JSONL (truth you can grep)
- Enable with
--audit --audit-dir audit - Logs include: auth successes/failures (mode, token type, subject, roles), rate‑limit drops (route, retry), plugin allow/deny events, and basic server health.
- Output file:
audit/audit.jsonl(one JSON per line, UTC timestamps).
Token upgrades (still simple)
- S1 (HMAC) tokens now support
aud(audience). Enforce with--require-aud "solveforce-phone"(CSV allowed). - Tokens may carry per‑plugin grants:
plugins: allow‑list of pluginsplugins_deny: deny‑list of plugins
These intersect with your step‑8 policy:final = policy(roles) ∩ plugins - plugins_deny.
- S2 (Ed25519) verification (optional). Provide public keys via
--ed25519-pub HEX(repeatable). We verify if PyNaCl or aned25519module is available. If not, S2 will be politely rejected (no_ed25519_lib).
Mint S1 token (CLI):
python solveforce_phone_nine.py --signing-secret "CHANGE_ME" \
--mint-token "sub=ron roles=reader,metrics dur=7200 aud=solveforce-phone plugins=battery,net"
Check token (CLI):
python solveforce_phone_nine.py --signing-secret "CHANGE_ME" \
--check-token "S1...."
Server‑side mint (admin):
GET /admin/mint?sub=ops&roles=reader,metrics&dur=3600&aud=solveforce-phone&plugins=battery,net&token=ADMIN123
Termux (Android) launch patterns
A) Adaptive polling + RBAC + auditing (HTTP)
python solveforce_phone_nine.py \
--host 0.0.0.0 --port 8080 \
--plugins-dir ~/solveforce/plugins \
--history-size 512 --strict-schema \
--poll 5 --poll-plugin battery:3 --poll-plugin net:10 \
--auth-mode protected \
--auth-token READER1:reader \
--auth-token NETOPS:reader,netops \
--policy-file /sdcard/solveforce/solveforce_policy_example.json \
--rate-policy-file /sdcard/solveforce/solveforce_rate_policy_example.json \
--audit --audit-dir /sdcard/solveforce/audit \
--allow-admin --admin-token ADMIN123
B) TLS + audience‑bound S1 tokens
python solveforce_phone_nine.py \
--host 0.0.0.0 --port 8443 \
--tls-cert server.crt --tls-key server.key \
--auth-mode strict \
--signing-secret "CHANGE_ME_LONG_RANDOM" \
--require-aud "solveforce-phone" \
--policy-file /sdcard/solveforce/solveforce_policy_example.json \
--rate-policy-file /sdcard/solveforce/solveforce_rate_policy_example.json \
--audit --audit-dir /sdcard/solveforce/audit
C) mTLS + optional S2 (Ed25519) verify
python solveforce_phone_nine.py \
--host 0.0.0.0 --port 8443 \
--tls-cert server.crt --tls-key server.key \
--tls-ca ca.crt --mtls-require \
--auth-mode strict \
--ed25519-pub <PUBKEY_HEX> \
--policy-file /sdcard/solveforce/solveforce_policy_example.json \
--rate-policy-file /sdcard/solveforce/solveforce_rate_policy_example.json \
--audit --audit-dir /sdcard/solveforce/audit
If the Ed25519 library isn’t present, S2 tokens will be rejected with
no_ed25519_lib. S1 (HMAC) remains available.
Route & role truth table (delta)
| Route | Needs role | RL policy key |
|---|---|---|
/events | reader | events |
/metrics | metrics | metrics |
/admin/* | admin + flag | admin |
/read /history | reader | read |
/state /plugins /schemas /validate /whoami /policies /authinfo | reader | introspect |
/ui | open only with --open-ui in protected | ui |
/health | open in protected; locked in strict | health |
WordPress — Step Nine section (drop‑in)
Step Nine — Adaptive cadence, policy throttles, audit
- Adaptive polling:
--poll, plus--poll-plugin name:sec. Backoff on error; decay to base on recovery. - Rate policy:
--rate-policy-filewithwindow_sec,routes{route:{max}}, androles{role:{mult|max}}. - Audit JSONL:
--audit --audit-dir auditlogs auth outcomes, policy allow/deny, and rate‑limit drops. - Token scope: S1 tokens now accept
audand optionalplugins/plugins_denylists (intersected with policy). - S2 tokens: Optional Ed25519 verify if libs exist (
--ed25519-pub HEX). - Philosophy: Observe more, guess less. Tolerate failure by stepping back; return to truth as conditions improve.
Integrity (publish these)
sha256sum solveforce_phone_nine.py
# e53c75e862432a516928e5a4b6e4d429d2f3965041b1b018a2642d802c808ad4
sha256sum solveforce_rate_policy_example.json
# 4842faf8bc32982ebd8090d5153505a3c8bf3466ec4f0521ec1ef13731bd3ca8
What I recommend for Step Ten
- Per‑plugin sampling policies (e.g., max QPS, jitter, burst permits) persisted in JSON.
- Audit roll‑up: hourly summaries of auth, policy, RL, and plugin health.
- Token‑embedded plugin caps (e.g., a token can read
batteryat 30/min regardless of role). - Server‑pushed “schema diffs”: detect and annotate changes in plugin payloads over time.
You’ve now got a roots‑to‑crown control loop: identity → policy → cadence → telemetry → audit. Exactly the kind of recursive discipline a legacy framework demands.
Step Ten — Sampling, Diffing, Caps, and the Hourly Ledger – SolveForce Communications