Skip to content

Discovered vs managed

A discovered resource is one your code reported but no one has configured yet. A managed resource is one someone has explicitly chosen to control. The platform tracks both so you can see what your code is actually doing without needing to pre-register everything — and only the things you choose to manage count against your subscription quota.

A concrete example

Your payments service has 200 logger categories from various libraries — sqlalchemy.engine.Engine, urllib3.connectionpool, boto3.resources, and so on. The smplkit logging SDK auto-discovers all of them on startup. They all show up in the developer console, dimmed, with their currently-resolved log levels visible — but none of them are "managed."

You only care about three: acme.payments, acme.payments.stripe_client, and urllib3.connectionpool. You click each one in the console and set a level. That click promotes those three to managed. They count against your logging.managed_loggers quota; the other 197 don't.

Two months later you stop debugging urllib3.connectionpool. You release it back to discovered — its level reverts to whatever the framework was producing originally, and your quota gets a slot back.

Why discovery exists

Two reasons:

  1. You can see what's running without registering it. The platform shows the actual loggers in your code, the actual flags your code declared, the services that have ever booted. No drift between "what's documented" and "what's deployed."
  2. You don't pay for things you don't configure. The 197 loggers you're not actively managing don't pull from your subscription quota. You only pay for what you actually control.

The model lets you start free, scale gradually, and avoid pre-registration ceremony.

Where discovery happens

ProductAuto-discovery?Default stateWhat's reported
LoggingYesmanaged = falseLoggers your framework knows about
FlagsYesmanaged = false for bulk-discovered, managed = true for explicit createsFlags your code declared with booleanFlag(...) etc.
ConfigNon/a — all configs are explicitn/a
EnvironmentsYesmanaged = false for SDK-discovered (classification = AD_HOC), managed = true for console / API creates (classification = STANDARD)New environment names the SDK reports via POST /api/v1/contexts/bulk

Logging, Flags, and Environments all auto-discover. Config does not — see the Config note.

Auto-discovery in flags and logging happens via POST /api/v1/flags/bulk and POST /api/v1/loggers/bulk. The SDK buffers declarations and flushes them to the bulk endpoint at startup, periodically (every ~60s), and at shutdown.

What promoting to managed does

The mechanics differ by product, but the user-visible effect is the same: the resource becomes configurable, counts against quota, and respects platform-controlled state instead of being read-only.

Logging

Two click paths in the console promote a logger:

  • Click the Default column cell and pick a level. The logger is set managed = true, level = <chosen>, environments = {} (apply everywhere).
  • Click an environment-specific cell and pick a level. The logger is set managed = true, level = "DEBUG" as the base, and environments[env].level = <chosen>.

The first promotion also runs a one-time descendant cascade: every dot-notation descendant that's still unmanaged (e.g. acme.payments.stripe_client under acme.payments) is marked with level = NULL so the resolution chain handles inheritance from the newly-managed ancestor.

The promotion is gated by your logging.managed_loggers quota. If you're at the limit, the API returns 402 Payment Required.

Flags

Promotion is implicit. There's no dedicated promote endpoint — when an admin edits a discovered flag in the console (saves any change), the PUT request includes managed: true and the platform records the new state. Once a flag is managed, you can configure environment-specific defaults, rules, and per-environment kill switches.

Note: unlike logging, all flags count against flags.items quota — both managed and discovered. The "discovered" state on a flag is a UI distinction (it's not yet configured), not a quota carve-out. See Flags concepts.

Config

Config doesn't yet have a discovered/managed split — see below.

Environments

Promotion is an explicit action — the standard PUT /api/v1/environments/{id} with managed: true in the body. The console exposes a Promote button per environment row. Unlike flags / loggers, promotion isn't implicit on edit; the environment lives in the app service while per-environment values live in the product services, and the cross-service slot consumption deserves a conscious customer act.

The promotion is gated by your platform.managed_environments quota. If you're at the limit, the API returns 402 Payment Required.

Until promoted, any product write that tries to set a per-environment value on the unmanaged environment is rejected with 400 environment_unmanaged.

production is special: it's always managed and cannot be demoted, so every account is guaranteed at least one writable environment.

What demoting to discovered does

Logging

Setting managed = false on a logger releases all platform control:

  • level cleared
  • environments cleared
  • group_id cleared (if assigned)

The logger reverts to discovered state and stops counting against quota. The SDK stops applying server-resolved levels to it; the framework's own level wins again.

Flags

There's no demote operation for flags. Once a flag is managed, it stays managed until you delete it. Deleting a flag removes it from your account; the next time the SDK reports it via auto-discovery, it'll come back as discovered.

Config

n/a — there's no managed/discovered concept.

Environments

Demoting an environment (managed = truefalse) is allowed for any non-production environment. Demotion is passively non-destructive: per-environment values across all product services (flag rules, config overrides, logger levels) are retained but frozen — runtime evaluations still see them, but new writes targeting the demoted environment get rejected with environment_unmanaged. Re-promoting later restores write access without data loss.

This makes demotion safe as a slot-management tool: a customer can park an environment to free a quota slot without losing the configuration that targets it.

Entitlement implications

Each product counts toward quota differently:

  • platform.managed_environments — counts only environments with managed = true. Unmanaged (AD_HOC/SDK-discovered) environments are free. Resolves at the highest plan held across any paid product.
  • logging.managed_loggers — counts only loggers with managed = true. Discovered loggers are free.
  • logging.groups — counts log groups (always created managed; no discovery for groups).
  • flags.items — counts every flag in the account, managed or not. The check is wired on POST /api/v1/flags, but ships -1 (unlimited) on every plan today, so flag count is effectively uncapped. Auto-discovery (POST /api/v1/flags/bulk) is exempt from governance entirely.
  • config.items — counts every config in the account.

When you hit a limit on a write that would push past it, the API returns:

http
HTTP/1.1 402 Payment Required
Content-Type: application/vnd.api+json

{
  "errors": [{
    "status": "402",
    "code": "entitlement_limit_reached",
    "title": "Subscription limit reached",
    "detail": "Your free plan allows a maximum of 15 managed loggers. Upgrade your subscription to increase this limit.",
    "meta": {
      "limit_key": "logging.managed_loggers",
      "current": 15,
      "maximum": 15,
      "plan": "free"
    }
  }]
}

See Subscriptions and entitlements.

Config doesn't have this yet

Config has no managed column, no POST /api/v1/configs/bulk endpoint, and no auto-discovery from the SDK. Every config is explicitly created (in the console or via the management API) and every config counts toward config.items quota.

This is a known parity gap with Flags and Logging. If and when discovery comes to Config, this page will be updated to describe it.

The closest thing to auto-creation in Config is the common config — a per-account default-parent config that's lazily created the first time you list configs through the API. See Config concepts.