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:

1

You name each feature in your code

Wrap whatever you want to control with a named SDK call — e.g. 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.
2

Same name on both sides = one feature

If your frontend and backend both touch the same feature, give it the same name in each. Onelo keeps one Registry entry per name and tags it with every platform that reported it — so a Swift desktop app and a Python backend that both check "ai-model" become a single feature badged PYTHON · SWIFT.
3

The SDK reports the names — in the Test feature environment

On startup (and whenever you click Discover Features) the SDK sends those names to Onelo (a 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.
4

A new name appears as Hidden; the dashboard updates live

A feature name Onelo hasn't seen before is added to the Registry with status 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-plugins

Install plugin

/plugin install onelo-features@onelo-tools

Update later (no reinstall)

/plugin marketplace update onelo-tools

Run it in your project

/onelo-features

After 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 writeWhat Onelo does
The same name on client and backend — e.g. "ai-model" in bothOne 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.
Rule of thumb: use the identical name across platforms when it's literally the same feature (one entry, both badges); use a shared prefix (ai-…, debug-…) for a related family you want to read as a group in the Registry.

The two gates

GateWhat it checksHow you satisfy it
Test environmentThe 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 instanceThe 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).
Both gates are checked on every 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:

EnvironmentUnknown slugKnown slug
TestRegistered as hidden (auto-restored if it was archived).Last seen refreshed; origin language recorded.
LiveSilently 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.

To add a feature to Live: discover it in Test (run your dev build), set it up, then Promote to Live and Deploy to Live. Production never grows the registry on its own.

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).

FieldWhere it comes from
Instance IDThe X-Onelo-Instance-Id header — the backend's ONELO_INSTANCE_ID, or a Swift install's generated id. Identifies the device / process.
Role / slotWhich declared slot the instance occupies (frontend or backend), shown with its expected platform.
PlatformThe X-Onelo-Sdk-Platform header (validated against an allow-list); the SDK User-Agent is the fallback. Python, Swift, JS, Kotlin, Flutter, Node, PHP.
1

First discovery → fills a slot

The first time an instance discovers (a test-env ping with ONELO_FEATURE_ENVIRONMENT=test), it fills a free slot and is shown here with a green Authorized · seen <time> ago. No manual approval step.
2

Unpin to reset

Click Unpin (you'll confirm). Unpin forgets the instance — its row is deleted and the slot frees up. It is a reset, not a permanent block: the next test-env discovery (from that instance or another) re-fills the slot automatically. To hand discovery to a different machine, unpin the old one and run discovery from the new one. Existing features are untouched.
Unpin is not a lockout — an unpinned instance that pings again in the Test environment re-authorizes itself (trust-on-first-use, again). The real off-switch for registry growth is the environment: production builds run without 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

LimitValue
Features per app200 — discovery stops adding once you hit the cap
New feature default statushidden — never visible without an explicit dashboard action
Deploy slots per app1 or 2 — from your app setup (standalone / backend-only = 1, frontend + backend = 2)
Discovery throttle50 discovery passes per 10 s per key — excess pings are ignored
Stale instance reclaimAn instance unseen for 30 days is removed automatically, freeing its slot

Next

Discovery & Deploy access — Features — Onelo Docs