Features / Discovery & Deploy access
Discovery & Deploy access
You don't add features to the dashboard by hand — your code does. When the SDK runs, it tells Onelo the feature names it references, and they appear in the Registry. That's discovery. Two things have to be true for it to write anything: the app is running in the Test feature environment (ONELO_FEATURE_ENVIRONMENT=test), and the device doing it is an authorized instance (deploy access).
How discovery works
You never type feature names into the dashboard — you write them in your code, and the SDK reports them. The flow:
You name each feature in your code
onelo.features.feature("advanced-export"). The string "advanced-export" is the feature's name (its slug). Put it wherever that feature lives — in your app, your backend, or both.Same name on both sides = one feature
"ai-model" become a single feature badged PYTHON · SWIFT.The SDK reports the names — in the Test feature environment
batch-ping). This only writes while the app runs with ONELO_FEATURE_ENVIRONMENT=test. On the backend you can also declare names up front with declare([...]) instead of waiting for the code to run.A new name appears as Hidden; the dashboard updates live
Hidden — so a half-finished feature stays invisible in your build until you set its status and deploy. A name Onelo already knows just gets its Last seen refreshed (and picks up the reporting platform's badge). New entries appear over the dashboard's live connection, no reload.Don't want to place every call by hand? The Onelo Features plugin for Claude Code scans your codebase, proposes feature names, and inserts the snippets for you — you just approve.
Add marketplace (one-time)
/plugin marketplace add onelo-tools/claude-pluginsInstall plugin
/plugin install onelo-features@onelo-toolsUpdate later (no reinstall)
/plugin marketplace update onelo-toolsRun it in your project
/onelo-featuresAfter updating, run /reload-plugins in Claude Code — no reinstall needed, snippets are fetched live. Running /onelo-features lets Claude propose feature names, you approve, and the snippets land in your code. The same commands live on your dashboard under SDK → Features.
One feature, two features, or a group?
Whether two SDK calls become one feature comes down to the name string:
| You write | What Onelo does |
|---|---|
The same name on client and backend — e.g. "ai-model" in both | One feature, tagged with every platform that reported it (PYTHON · SWIFT). |
Two different names — e.g. "ai-model" and "chat" | Two separate features — Onelo has no way to know they're related. |
Related names that share a prefix — e.g. "ai-model" (client) and "ai-model-api" (backend) | Still two separate features — but Group by → Slug prefix folds everything before the first - into one heading (here ai), so you can see they belong together. A whole debug-* family groups under debug. |
ai-…, debug-…) for a related family you want to read as a group in the Registry.The two gates
| Gate | What it checks | How you satisfy it |
|---|---|---|
| Test environment | The effective environment resolves to test — that's set by ONELO_FEATURE_ENVIRONMENT, not by the key. | Set the feature environment to test on the instance. |
| Authorized instance | The instance (device / process) sending the ping holds one of the app's deploy slots. | Trust-on-first-use within slots — the first instance to discover fills a free slot; instances beyond the slots are silently ignored. Manage them in Deploy access (below). |
batch-ping. A missing instance id is rejected (the SDK must send X-Onelo-Instance-Id); a live environment never registers, no matter the key.Why discovery is Test-only — and what Live does
Discovery writes only in Test. In Live the same batch-ping is read-only:
| Environment | Unknown slug | Known slug |
|---|---|---|
| Test | Registered as hidden (auto-restored if it was archived). | Last seen refreshed; origin language recorded. |
| Live | Silently ignored — the SDK keeps working, the registry stays clean. | Last seen refreshed only. No inserts, no un-archiving. |
This is deliberate. Live features must arrive through one controlled door — Promote from Test — not by passively accumulating from production traffic. It keeps typos (isEnabled("chta")) out of your live registry and avoids broadcasting churn to every production instance.
The “Discover Features” button
In the Registry (Test env) the Discover Features button forces a fresh batch-ping from your already-running, connected instances over their live connection. It's a convenience for when your app is up and you want it to re-report now — it is not a required gate. Any authorized test-key ping registers new slugs whether or not you pressed it.
When new names come in, a short summary appears: it lists what was discovered and lets you choose the status to apply (default: keep as hidden). On Live the button is disabled with a hint — switch to Test, discover, then promote.
Deploy access — who is allowed to register
The Deploy access panel (Registry, Test env) lists the instances authorized to register features — its subtitle: "Instances allowed to register features. Slots come from your app setup; first one to discover fills its slot — unpin to reset." Binding is key-transparent: it hangs off the instance identity scoped to this app, not the API key — so a client app and its backend each authorize independently, even when they share a key.
Authorization is slot-based: your app setup declares one or two slots — a frontend slot, a backend slot, or both (frontend + backend setups). Each slot is filled by the first matching instance that discovers; any instance beyond the declared slots is silently ignored (a clean 204, never an error — so a stray CI box can't retry-storm the endpoint, and the slot occupants keep owning discovery).
| Field | Where it comes from |
|---|---|
| Instance ID | The X-Onelo-Instance-Id header — the backend's ONELO_INSTANCE_ID, or a Swift install's generated id. Identifies the device / process. |
| Role / slot | Which declared slot the instance occupies (frontend or backend), shown with its expected platform. |
| Platform | The X-Onelo-Sdk-Platform header (validated against an allow-list); the SDK User-Agent is the fallback. Python, Swift, JS, Kotlin, Flutter, Node, PHP. |
First discovery → fills a slot
ONELO_FEATURE_ENVIRONMENT=test), it fills a free slot and is shown here with a green ● Authorized · seen <time> ago. No manual approval step.Unpin to reset
ONELO_FEATURE_ENVIRONMENT=test, so they can never register anything. Unpinning also never revokes the app's ability to read features. (Rows from the older revocation model show as Blocked (legacy) — their pings are silently ignored until you unpin to clear them.)Limits
| Limit | Value |
|---|---|
| Features per app | 200 — discovery stops adding once you hit the cap |
| New feature default status | hidden — never visible without an explicit dashboard action |
| Deploy slots per app | 1 or 2 — from your app setup (standalone / backend-only = 1, frontend + backend = 2) |
| Discovery throttle | 50 discovery passes per 10 s per key — excess pings are ignored |
| Stale instance reclaim | An instance unseen for 30 days is removed automatically, freeing its slot |