Syntra is a self-hosted HTTP appliance for adaptive operational decisions. Each capsule is a Lycan program. The program can compute from the data it sees — EWMA forecasts on a recent series, percentile / stddev statistics, autoscale recommendations, sandboxed HTTP / SQL / file I/O — and run a strategy node over a fixed list of options. Syntra learns which option works best for each context from the delayed feedback you POST back to it.
The full positioning, including what Syntra is and is not, lives in POSITIONING.md. Read it before installing if the repositioning matters to you.
This repository (SectorOPS/Syntra) is the deployable product — the
appliance you install and run. The Lycan language runtime ships here as
a vendored subdirectory at Lycan/; a fresh git clone of
this repo is fully self-contained and builds without any other checkout.
A separate repository, SectorOPS/Lycan,
is the canonical home for the Lycan language itself. Language work
(new kernels, parser changes, runtime semantics, capsule format) starts
there; updates flow into the vendored Lycan/ subdirectory of this repo
on a periodic cadence. Anyone building or deploying Syntra clones only
this repo — the Lycan repo is for people working on the language.
What that buys you, in concrete operational terms:
- A choice that's informed by computation, not only by features the
caller hand-built. A capsule can read a recent load history out of
the request body, forecast it forward one step with
series.ewmaForecast, derive the recommended instance count viaops.autoScaleRecommend, and run a strategy node over four scaling policies — all inside one inspectable graph. The three new demos underexamples/predictive-autoscaling/,examples/anomaly-routing/, andexamples/seasonal-fraud-threshold/walk through this pattern end to end. - Auto algorithm selection on the choice layer. A meta-bandit runs seven candidates in parallel (Thompson, UCB1, EpsilonGreedy, Weighted, Greedy, LinUCB, LinTS) and converges on whichever performs best on your traffic. You don't pick.
- Drift detection. Capsule-level and per-context ADWIN detectors catch regime shifts and re-warm the learner without losing the rest of the capsule's state.
- Confidence-based refusal. When the bandit isn't confident —
out-of-distribution input, prediction interval too wide —
/decidereturns a refusal so your service can fall back to its default policy. Configurable; disabled by default. - Operational hardening. Scoped auth tokens, token-bucket rate
limit, Prometheus
/metrics,/readystore-writability probe, JSON structured logging, backup/restore via JSON bundles. Run behind a TLS proxy.
Use it for repeated operational decisions where the best option depends on context and the outcome arrives later: how aggressively to scale a service in response to a recent load history; which routing policy to use when latency is anomalous; which fraud threshold band to apply when the chargeback resolves a week later; which LLM model handles this request; which retry or timeout policy this customer path uses; which queue / route / ranking / threshold wins for this job; which strategy works for this tenant or region.
Capsules are authored as .lycs (Lycan source) and compiled to .lyc.
The runtime exposes 26 Rust-native capability kernels; the ones that
matter for the operational use cases above:
| Package | Kernels |
|---|---|
| math | stats.mean, stats.stdDev, stats.min, stats.max, stats.percentile |
| math | series.ewmaForecast (one-step EWMA forecast) |
| ops | ops.autoScaleRecommend |
| net | http.get, http.post (allow-listed hosts, private networks denied) |
| data | sql.sqliteQuery (read-only SELECT/WITH/PRAGMA), json.get/has/len |
| io | file.readText, file.writeText, file.exists (sandboxed) |
| runtime | runtime.input, runtime.inputGet |
Every call is policy-enforced at the runtime layer. Full registry and
sandbox semantics in
Lycan/src/capabilities.rs;
the Lycan README
groups them in a table.
# Pull and run
docker run -d \
--name syntra-demo \
-p 8080:8080 \
-p 8787:8787 \
ghcr.io/sectorops/syntra:demo
# Access
# Dashboard: http://localhost:8080
# API: http://localhost:8787That's it. Five demo capsules are pre-installed and a traffic generator drives one of them; open the dashboard to watch the lifecycle flip from Warmup to Active in the first minute and the meta-bandit panel populate trials across the seven candidate algorithms in the first five.
- Predictive autoscaling — EWMA forecast + adaptive scaling policy
- Anomaly-aware API routing — latency z-score + adaptive fallback
- Seasonal fraud threshold — EWMA on fraud rate + threshold policy
- Shared-state action embeddings — LinUCB generalization across actions
- Hierarchical region routing — nested decisions with per-level learning
The
:demotag is built and pushed by a GitHub Actions workflow on push tomain. Until that workflow has run for the first time the image is not pullable — build from source per the Local Development guide.
For production deployment, see the Helm chart or Terraform modules. For local development, see Local Development.
The retry-tuning example is the canonical Python
integration. Drop-in for requests:
from syntra_retry import RetryClient
client = RetryClient(
syntra_url="http://localhost:8787",
capsule_path="/tenants/myteam/jobs/retry/capsules/router",
admin_key=os.environ["SYNTRA_ADMIN_KEY"],
)
response = client.request("GET", "https://api.example.com/users")Every request goes through /decide to pick a retry policy, then /feedback
with success and latency. The client falls back to a configured default when
Syntra is unreachable, refuses, or returns a malformed response — a Syntra
outage degrades adaptive retry to "always fall back" without breaking the
request flow.
See examples/retry-tuning/README.md for setup,
customization, and tests.
Repeated operational decisions where outcomes resolve after the decision:
- Predictive autoscaling — read a recent load history, forecast it
forward, derive candidate instance counts, learn which scaling policy
wins for which traffic shape. See
examples/predictive-autoscaling/. - Anomaly-aware API routing — derive a z-score from a recent
latency-series window, learn when to fall back from
primarytosecondarytodegraded_cache_onlytocircuit_break. Seeexamples/anomaly-routing/. - Seasonal fraud thresholds — EWMA-forecast next-window fraud rate,
pick a threshold-adjustment policy, learn from chargebacks that
resolve days later. See
examples/seasonal-fraud-threshold/. - LLM model routing — pick
cheap_fastvsbalancedvsexpensive_accurateper request, learn quality / latency / cost tradeoffs per context. - HTTP retry policy — pick a retry strategy per endpoint based on recent failure rate and p99 latency. (See the demo and the integration example.)
- Queue / route / ranking selection — pick which downstream handler or ranking weight wins for this customer or context.
It is not for:
- Arbitrary forecasting — Syntra ships exactly one forecasting
kernel (
series.ewmaForecast, one-step EWMA with onealphaparameter). It is not a substitute for a proper time-series model. - A managed service — self-hosted Docker container, single process, local-filesystem store. Run behind a TLS proxy.
- Modern-data-stack scale — designed for hot-path decisions in the hundreds-to-low-thousands per second on commodity hardware. No clustering.
- A metric collection / observability system — the optional
sidecar/reads from Prometheus / Datadog / SQL but does not store time series or replace those tools. - A model platform — no GPU, no training loop, no model registry, no fine-tuning.
- Supervised problems with ground-truth labels at prediction time — use a model framework.
- Continuous-valued action spaces — Syntra picks among discrete
options. (A bucketed
ActionSpace::Continuousexists for the continuous-bucket case; see Phase G+H entry in CHANGELOG.) - One-shot decisions — without a feedback loop, there's nothing to learn.
- A replacement for experiment / feature-flag platforms — those tell you whether to ship X; Syntra picks which option to use once X is shipped. Adjacent to Statsig / Eppo / GrowthBook / LaunchDarkly, not a replacement.
Each capsule moves through a lifecycle: Warmup → Active → Frozen.
- Warmup — Syntra runs uniform random selection for the first ~30 feedback rounds, watches reward shape, and characterizes the problem (binary / continuous / sparse). It picks an initial algorithm automatically.
- Active — a rate-adaptive meta-bandit runs seven candidate algorithms in parallel: Thompson, UCB1, EpsilonGreedy, Weighted, Greedy, and (for feature-context capsules) LinUCB and LinTS. The meta-bandit converges on whichever candidate performs best on this capsule's data.
- Frozen — operator-triggered; the bandit stops learning but continues serving decisions from the current weights.
Drift detection runs at two scopes: a capsule-level ADWIN detector triggers re-warmup when reward distribution shifts globally, and per-context ADWIN detectors reset just the affected context bucket on narrower shifts.
Refusal (Phase E, opt-in) wraps reward predictions in split-conformal
intervals and tracks out-of-distribution scores per context. When the
interval is too wide or the input is OOD, /decide returns
{"refused": true, "confidence": {…}} and your service falls back. See
refusal config below.
A learning.json per capsule controls the learner. Most fields default to
sensible values; the ones you usually touch:
{
"contextSpec": {
"type": "features",
"features": [
{"name": "recent_failure_rate", "type": {"kind": "continuous", "range": [0, 1]}},
{"name": "p99_latency_ms", "type": {"kind": "continuous", "range": [0, 5000]}},
{"name": "hour", "type": {"kind": "cyclic", "period": 24.0}}
]
},
"refusal": {
"enabled": true,
"coverage": 0.95,
"maxIntervalWidth": 0.5,
"oodThreshold": 0.8
}
}contextSpec—discrete(stringcontextKey, the default) orfeatures(typed vector; enables the LinUCB candidate in the meta-bandit).refusal— enabled-off by default. When on, the response carries aconfidenceblock withoodScore,intervalWidth, andrefused: bool.
PUT it at any time: PUT /tenants/{t}/jobs/{j}/capsules/{c}/learning.
# Install a capsule
curl -X POST http://localhost:8787/tenants/acme/jobs/routing/capsules/router/install \
-H "Authorization: Bearer $LYCAN_ADMIN_KEY" \
--data-binary @router-capsule/program.lyc
# Optional: install the reward spec so feedback can use the components form
curl -X PUT http://localhost:8787/tenants/acme/jobs/routing/capsules/router/reward_spec \
-H "Authorization: Bearer $LYCAN_ADMIN_KEY" \
-H "Content-Type: application/json" \
--data-binary @router-capsule/reward_spec.json
# Get a decision (discrete-context capsule)
curl -X POST http://localhost:8787/tenants/acme/jobs/routing/capsules/router/decide \
-H "Authorization: Bearer $LYCAN_ADMIN_KEY" \
-d '{"contextKey":"support-low-cost"}'
# → response carries decisionId, decisions[], oodScore, refused, confidence
# Get a decision (feature-context capsule)
curl -X POST http://localhost:8787/tenants/acme/jobs/routing/capsules/router/decide \
-H "Authorization: Bearer $LYCAN_ADMIN_KEY" \
-d '{"features":{"recent_failure_rate":0.15,"p99_latency_ms":1200,"hour":3.0}}'
# Send feedback
curl -X POST http://localhost:8787/tenants/acme/jobs/routing/capsules/router/feedback \
-H "Authorization: Bearer $LYCAN_ADMIN_KEY" \
-d '{"decisionId":"dec_abc123","reward":0.85}'
# Inspect learned state
curl http://localhost:8787/tenants/acme/jobs/routing/capsules/router/report -H "Authorization: Bearer $LYCAN_ADMIN_KEY"
curl http://localhost:8787/tenants/acme/jobs/routing/capsules/router/memory -H "Authorization: Bearer $LYCAN_ADMIN_KEY"
curl http://localhost:8787/tenants/acme/jobs/routing/capsules/router/contexts -H "Authorization: Bearer $LYCAN_ADMIN_KEY"See docs/api.md for the full surface including evolution, chaos, evaluate, and audit endpoints.
Capsules are authored as YAML and compiled to a deployable .lyc by the
syntra author command:
name: llm-router
options:
- cheap_fast
- balanced
- expensive_accurate
reward:
type: continuous
range: [-1.0, 1.0]syntra author my-capsule.yaml --out-dir ./my-capsule/
# emits my-capsule/program.lyc + sidecar JSONThen POST program.lyc to /install and PUT a learning.json to attach a
feature-context spec or enable refusal.
Smoke-test a spec locally before deploying:
syntra simulate my-capsule.yaml --rounds 5000 --true-arm-rewards "0.2,0.5,0.7" --seed 7tenant / job / capsule
tenant = organization or environment
job = independent learning context (same capsule, different memory)
capsule = the compiled program + its learned state
syntra-store/
tenants/{tenant}/jobs/{job}/capsules/{capsule}/
current.lyc — the graph binary
policy.json — runtime permissions
memory.json — learned weights, meta-bandit, calibrators, OOD detectors
learning.json — algorithm config (contextSpec, refusal, …)
warmup.json — lifecycle state
audit.jsonl — mutation log
decision.jsonl — decision log (carries refused flag and confidence)
feedback.jsonl — feedback log
snapshots/ — pre-mutation backups
Container is disposable. The store survives restarts. The memory.json
schema is at version 7, with backward-compat readers for v2 through v6.
Syntra can run beside an existing application without taking control:
- Your app sends request context to
/decide. - Syntra returns a suggested option and a
decisionId. - Your app continues with its current production decision.
- When the real outcome resolves, your app posts
/feedbackwith thedecisionIdand the observed reward. - Syntra updates memory and exposes the learned state in
/report,/contexts, the admin console.
That makes it possible to prove the adaptive layer before letting it influence live behaviour.
Browser UI at /admin:
- Tenant / job / capsule navigation
- Live strategy weight visualization
- Decision and audit log inspection
- Policy enforcement status
- Context memory viewer
- Capsule deletion and log purging
- All routes except
/healthrequireAuthorization: Bearertoken. - Capsule policy enforced at runtime (file sandbox, network sandbox, SSRF protection).
- File capabilities scoped to capsule working directory.
- HTTP capabilities require explicit
allowed_hosts. Private networks denied by default. - Constant-time key comparison. Failed auth logged.
- Server refuses startup without an admin key unless
--dev-mode(binds localhost only).
Not yet production-hardened for direct public-internet exposure. Run behind a TLS proxy. The path to production hardening is tracked in #2 and starts with the threat model in SECURITY.md.
When weights look wrong, inspect the data trail before changing the capsule:
/reportfor current strategy weights./contextsto confirm the request landed in the expectedcontextKey.decision.jsonlfor what Syntra suggested.feedback.jsonlfor which option was rewarded and whether the reward sign is correct.audit.jsonlfor installs, policy changes, deletes, refusals, and change-detection events.
See docs/operating.md for the full operator checklist and docs/deployment.md for production deployment.
Syntra is currently running in shadow mode against MoEfolio.ai, a public AI trading panel that produces a verdict per cycle and resolves outcomes against the market after a delayed window.
Across recent verdicts, Syntra has learned non-trivial weights against the panel's gate and is expressing structured disagreements, primarily that the gate may be over-cautious on some BUY signals. Whether those disagreements are correct requires more resolved outcomes than are currently available; the experiment is ongoing.
Syntra is built on Lycan, a graph-execution runtime that ships in this
repo as the Lycan/ subdirectory. Capsules are authored as
YAML and compiled to Lycan's binary format automatically — most Syntra
users never interact with Lycan directly. If you want to dig into the
substrate, the source lives at Lycan/src/ and the
language reference at Lycan/README.md.
Operational-kernel demos — series.ewmaForecast, stats.percentile,
stats.mean / stdDev, ops.autoScaleRecommend feeding into an adaptive
choice. Each ships a capsule.yaml, a program.lycs, a learning.json,
and a README walking through install / decide / feedback.
examples/predictive-autoscaling/— EWMA forecast + autoscale-recommend driving a four-policy scaling choice.examples/anomaly-routing/— mean / stddev / z-score driving a four-policy routing choice.examples/seasonal-fraud-threshold/— EWMA forecast on a fraud-rate series driving a four-policy threshold choice.
Integration packs — Python and language-client examples consuming Syntra over HTTP:
examples/retry-tuning/— canonical Python integration library and tests.examples/fraud-tuning/,examples/queue-selection/,examples/llm-routing/— sister domain packs.examples/syntra-go/,examples/syntra-node/,examples/syntra-java/,examples/syntra-rs/— language clients.
Bash demos and tooling:
examples/demo-llm-model-routing.sh— three model routes, two contexts, persistence across restart.examples/demo-static-policy-vs-syntra.sh— focused static-vs-adaptive proof.examples/offline-eval/— IPS and doubly-robust off-policy estimators.examples/ab-harness/— A/B simulation harness.
Substrate-level demos (read these if you want to see the Lycan kernels exercised directly, not through a Syntra capsule):
examples/lycan-internals/— autoscaler, capability-pack, webhook-load demos.
sidecar/ — syntra-ingest, an optional metrics-ingestion
sidecar. YAML-configured, polls Prometheus / Datadog / SQL / file sources
on a per-source interval, exposes GET /features/current returning the
latest snapshot. Best-effort, stateless, single-process. Use it if your
capsule needs feature values that live in those systems and you don't
want to embed four client libraries inside your hot path. Not a metric
store.
- POSITIONING.md — the canonical statement of what Syntra is and is not.
- PITCH.md — the under-1000-word sendable pitch.
- docs/concepts.md — contextual-bandit concept doc.
- docs/concepts/operational-intelligence.md — the kernel-feature-derivation-to-strategy-node pattern this README leads with.
- ROADMAP.md — short version of upcoming work.
- CHANGELOG.md — what shipped in each phase.
- Apache-2.0.