Documentation

Operate Konvoa with the whole system in view.

Use this guide to configure an application, ship a signed client, manage customers and collaborators, and operate releases without handing authority to the desktop process.

Start with one application

Overview

One control plane, four trust boundaries

Konvoa is the current server-side authority for a desktop application's accounts, licenses, devices, sessions, subscriptions, and release metadata. A shipped client proves possession of an installation key and verifies the server's signed response. It never decides its own entitlement.

Workspace
The owner account, plan, teammates, reseller relationships, and applications.
Application
The isolation boundary for customer identities, policies, versions, files, and access records.
Desktop session
A short-lived, application-bound authorization context tied to a device key.
External artifact
Owner-hosted bytes identified by a Konvoa file ID and verified SHA-256.

Quick start

From an empty workspace to a verified login

  1. 1

    Create the application

    Open the console, create an application, and keep HWID Lock enabled unless multi-device access is intentional. Copy the public app_… identifier.

  2. 2

    Define the entitlement

    Create at least one subscription tier, then issue a test license with an explicit duration. A customer consumes that license during registration.

  3. 3

    Integrate a client

    Use the maintained C++ SDK or download a reference client for C#, Python, Node.js, Go, Rust, or PHP. Keep its persistent device key and response-key pins intact.

  4. 4

    Publish the exact build

    Register a strict SemVer version and the SHA-256 of the artifact you ship. When its file is eligible, the console selects minimum-version enforcement by default; clear that choice only when a staged rollout is intentional.

  5. 5

    Test failures before release

    Exercise a revoked license, changed device, expired session, replayed nonce, wrong response pin, outdated version, disabled app, and mismatched artifact digest.

Core model

IDs are stable; authorization is scoped

Use opaque IDs exactly as returned. Do not infer ownership, creation time, or permissions from an identifier. Customer usernames are unique inside an application, while the underlying identity remains isolated from users with the same display name in another app.

ResourceIdentifierScopeKeep secret?
Applicationapp_…WorkspaceNo
License recordOpaque key IDApplicationNo
Raw licenseConfigured key formatCustomer activationYes
User and sessionOpaque user ID / SIDApplicationTokens are secret
Filefile_ plus 32 lowercase hexadecimal charactersApplicationNo
Device key IDEd25519 public-key thumbprintInstallationPrivate key is secret

Applications

The application is the operating boundary

Create a separate application for each independently shipped product or environment. Its users, licenses, subscriptions, variables, files, versions, sessions, reseller access, and security policy stay scoped together.

Status
Active accepts eligible logins. Disabled blocks new authorization and should be used for an immediate product-wide stop.
Public ID
Compile this identifier into the client. It selects an app but grants no authority by itself.
Session lifetime
Set the duration deliberately. Shorter sessions reduce stale access; refresh rotation keeps active customers signed in.
Archival
Removing an application is a high-impact operation. Export required operational records and confirm the selected app before proceeding.

Licenses

Issue access, then manage the record

A license starts unused, is consumed by registration, and then remains connected to the resulting user and subscription. The console masks raw keys at rest in the interface, permits a rate-limited reveal only when authorized, and supports selection-based management.

Create
Choose a subscription, quantity, and duration unit. Use a custom expiration date when access must end at local end-of-day on a specific date.
Extend
Add time to the active entitlement without issuing a replacement identity.
Revoke
Stops authorization and closes dependent sessions. Treat revocation as an access-control event, not a visual status change.
Device reset
Clears the active binding so the legitimate customer can establish a new one on the next authenticated flow.
Automation must be retry-safe

Management operations that support Idempotency-Key should receive a unique value for the business action and reuse that same value only when retrying that action.

Users and sessions

Customer state and live access stay connected

Use the Users view for durable account state and the Sessions view for currently authorized clients. Extending or subtracting expiration updates entitlement time. Disabling or deleting a user can also revoke its license when that explicit option is selected.

ExpirationAdd or subtract a bounded number of days, weeks, months, or years for selected users.
Selected usersApply bulk expiration or disable actions to checked records.
All usersUse the application-wide disable operation only after its destructive confirmation.
SessionsReview device and activity context, then revoke one session without changing the account.
BanBlocks the identity according to current server policy and invalidates access.
Reset deviceAllows a new binding while preserving the user and entitlement.

Subscriptions and variables

Model what a customer receives

Subscriptions are application-scoped entitlement tiers. Licenses select a tier, users inherit the resulting access, and files can be restricted to selected tiers. Use a clear default tier for the common case and reserve additional tiers for meaningful product differences.

Variables expose small owner-managed values to signed clients. They are suitable for configuration flags and public runtime settings, not for private service credentials. Any value returned to a customer-controlled process must be treated as observable.

Access controls

Policies are evaluated on the server

Application status

Disable the app to stop eligible users from creating new authorized sessions. Restore it only after the incident or maintenance window is resolved.

Immediate product-wide control

HWID Lock

Enabled by default. The service binds a user to its installation identity and requires an owner reset when a different device attempts to replace it.

Server-side device binding

VPN policy

Off skips the provider decision, Monitor records a result without blocking, and Enforce rejects a provider-classified VPN. Enforcement is unavailable until the configured intelligence provider is healthy.

Provider-backed network policy

Hash Check

Monitor compares the signed client-reported build digest with the published artifact. Enforce can reject a mismatch. This is tamper telemetry, not hardware attestation.

Release-integrity signal

Versions and files

Publish metadata; keep release bytes on your host

A version records strict SemVer, channel, lifecycle status, release notes, and an optional file. An application can hold multiple files, each with its own stable ID, editable direct HTTPS URL, expected SHA-256, content metadata, status, and subscription mapping.

The Files inventory supports row selection, select-all up to the 100-record bulk limit, label and link editing, digest and metadata editing, subscription access changes, individual deletion, and atomic bulk deletion. Deletion permanently removes an unreferenced file record, cancels its pending download tickets, and releases plan capacity. A file referenced by any draft, published, or archived version must first be removed from that version; published artifact history remains immutable.

  1. 1

    Create a file record

    Provide a label, direct HTTPS URL, complete lowercase SHA-256, and optional tier mappings. Konvoa stores metadata only.

  2. 2

    Publish a version

    Attach the exact file and digest for the build. For a published version whose file is active, has a valid SHA-256, and is available across subscriptions, “Enforce this as the minimum version” is selected by default.

  3. 3

    Confirm the policy

    The console publishes an eligible version and applies Enforce in the same server transaction. Draft, unavailable, invalid, or tier-restricted artifacts do not auto-enforce, and the server validates every policy change. Monitor reports adoption without blocking; Off removes the minimum decision.

  4. 4

    Recover an outdated client

    The authenticated client retrieves signed minimum-version metadata and then downloads from the owner's host with a fresh credential-free request.

Konvoa is not a file host

Konvoa does not accept upload bytes, fetch the publisher URL, proxy content, or forward credentials to the external host. The client must reject redirects, require HTTP 200, bound the transfer, and verify the complete SHA-256 before use.

SDKs and examples

Start from code that already verifies the protocol

The maintained C++ SDK includes the full native client, self-contained Visual Studio x64 solution, bundled libsodium headers and static libraries, response pins, one-click build script, and beginner starter. Reference clients demonstrate the signed login exchange in six additional languages.

C++ beginner build

1. Extract the complete ZIP without moving individual folders.
2. Edit starter/app_config.hpp and replace PASTE_APP_ID_HERE.
3. Set kApplicationVersion to this build's strict SemVer.
4. Double-click BUILD-WINDOWS.cmd.
5. Run out\Release\x64\konvoa-starter.exe.

The ordinary Visual Studio build needs no vcpkg install, SODIUM_DIR, DLL copy, or manual include and library paths. Verify every archive against SHA256SUMS before extracting it.

Desktop API

Signed client operations

The desktop base path is /api/v2/desktop. Every operation uses the signed-request protocol. “Session” below means an access token bound to the application, user, device ID, and device-key thumbprint.

MethodPathAuthorizationPurpose
POST/auth/bootstrapDevice proofRegister the installation's public device key through the bootstrap flow.
POST/simple/registerDevice proofConsume a license and create an application-scoped customer.
POST/simple/loginDevice proofAuthenticate and issue the bound access and refresh session.
POST/auth/refreshRefresh proofRotate the refresh credential and receive a new access context.
GET/license/checkSessionRecheck current license, identity, scope, product, and risk state.
POST/simple/subscription/checkSessionResolve active entitlement for a product subscription.
GET/simple/var/{name}SessionRead an application-scoped client variable.
GET/simple/version-policySessionRead the signed minimum-version decision, including during update recovery.
GET/simple/version-updateSessionResolve the minimum version's authorized external artifact metadata.
GET/simple/download-link/{file_id}SessionResolve an entitled external file URL and its optional SHA-256 integrity pin.
POST/simple/self-banSessionDisable the current authenticated context for an application-defined critical response.

Use the SDK for request construction and response verification. The route list does not replace canonical signing, replay protection, response pins, bounded parsing, or secure credential storage.

Signed protocol

Sign the exact bytes that cross the boundary

Canonical request v2 binds the key ID, application ID, strict application version, optional executable digest, uppercase method, exact origin-form path and query, lowercase host, UUID request ID, Unix timestamp, random nonce, bearer-token digest, and exact body digest. Konvoa verifies freshness and one-time replay state before protected mutation.

The response signature binds its response-key ID, method, unchanged path and query, HTTP status, original request ID, server timestamp, and exact response-body digest. Verify that signature against a release-authenticated pin before parsing protected JSON or trusting an error's recovery metadata.

Errors and recovery

Branch on verified codes, not message text

StatusMeaningClient response
400Malformed or inconsistent signed inputStop and correct the integration. Do not retry unchanged.
401Invalid credentials, token, session, or proofClear terminal credentials when required, then perform a clean sign-in.
403Current policy denied the operationShow an appropriate access message without exposing policy internals.
404Resource is absent or intentionally hidden by scopeDo not use the difference to infer another application's resources.
409State conflict or replay-sensitive operationFetch current state and decide whether a new business action is valid.
410A retired compatibility flow was requestedMove to the documented replacement rather than retrying.
426AUTH_UPDATE_REQUIREDVerify the response, pause ordinary work, then use the signed version recovery flow.
429Network or subject rate limitApply bounded backoff and avoid parallel refresh or login storms.
503Maintenance or a required policy provider is unavailableFail closed and retry later with a bounded schedule.

AUTH_OWNER_RESET_REQUIRED means a locked user's device can only be cleared by an authorized application operator. Never work around it by silently replacing local identity material.

Teams

Delegate work without transferring ownership

Invite teammates into the workspace and assign the smallest role that covers their job. Application-scoped access limits which products a member can inspect or change. Workspace-wide permissions are reserved for duties such as team membership, billing, and reseller administration.

Invitation
Send to the intended account email, review the role and application scope, and revoke unused invitations.
Role change
Takes effect against current server authorization. Review active sessions after a material reduction in access.
Application scope
Select only the apps the member operates. Empty or broad scope must never be treated as implicit authorization.
Removal
Ends workspace membership without deleting the underlying account or the owner's application data.

Resellers

Separate delegated sales from workspace administration

An owner grants a reseller access to selected applications and selected actions. Per-application balances represent the units available for issuance. The reseller portal exposes only records inside that grant and keeps its inventory and customer operations separate from platform administration.

App accessExplicitly select which applications the reseller can serve.
PermissionsGrant issuance and customer actions independently wherever the role model permits.
BalancesEdit permitted application and duration pools together with one required reason; the batch commits atomically to the immutable ledger.
LifecycleSuspend and reactivate access without destroying credential state. Deletion hides the identity, blocks login, reserves its username, and retains delivery and ledger evidence.

Reseller automation

Automated delivery without pre-stocking keys

An application owner can opt a reseller into on-demand license delivery for one selected application. The API issues a fresh key during each delivery instead of forcing the reseller to pre-generate or stock keys. Credentials are separate from browser sessions and desktop-user tokens.

Owner credential management

MethodPathPurpose
GET/api/v2/console/resellers/{reseller_id}/api-credentialsList credential metadata. Secrets and secret hashes are never returned.
POST/api/v2/console/resellers/{reseller_id}/api-credentialsCreate a scoped credential. The complete secret is returned once.
POST/api/v2/console/resellers/{reseller_id}/api-credentials/{credential_id}/revokeReversibly stop a credential so future deliveries fail.
POST/api/v2/console/resellers/{reseller_id}/api-credentials/{credential_id}/reactivateReactivate an unexpired credential after its live application grants are revalidated.
DELETE/api/v2/console/resellers/{reseller_id}/api-credentials/{credential_id}Delete the credential from management while retaining immutable delivery evidence.
POST /api/v2/console/resellers/{reseller_id}/api-credentials
Content-Type: application/json
X-CSRF-Token: <workspace-session-csrf-token>

{
  "name": "Store delivery",
  "application_id": "app_0123456789abcdef0123",
  "subscription_ids": ["sub_standard"],
  "scopes": ["licenses:deliver"],
  "expires_at": 1798761600
}

The signed-in actor must be the workspace owner or a full-workspace administrator with reseller-management permission. Credential creation does not ask for a password again, but it still requires a valid session with at least five minutes remaining, a same-origin JSON request, the session's CSRF token, and the credential-creation rate limit. Every lifecycle change is audited. Choose between 1 and 25 subscriptions, or omit subscription_ids to bind every subscription the reseller can currently issue for the application. Expiration defaults to 90 days, must be at least five minutes away, and cannot exceed 365 days. A reseller can hold at most 10 active automation credentials.

The HTTP 201 response wraps credential, secret, and secret_available_once: true inside data. Store the returned kvo_rs_v1.… secret in a server-side secret manager; subsequent lifecycle responses expose only its masked preview and never the secret or its hash. Reactivation uses the original secret and is rejected when the credential expired, the reseller is suspended, an application grant changed, or the active-credential limit is full.

Credential metadata contains credential_id, reseller and application IDs, name, secret_preview, scopes, subscription IDs, stored status, effective status, expiry, last-use time, creation and update times, and revocation time. Listing returns non-deleted records in data.items. Deletion is an auditable tombstone, not a physical row removal, because delivery and idempotency journals retain restrictive references to the credential.

Create a delivery

POST /api/v2/reseller-api/v1/deliveries
Authorization: Bearer kvo_rs_v1.<credential_id>.<secret>
Idempotency-Key: ord_7Jf3Qz9mP2vK8sRc4nTx6w
Content-Type: application/json

{
  "subscription_id": "sub_standard",
  "customer_reference": "ord_7Jf3Qz9mP2vK8sRc4nTx6w",
  "duration_unit": "month",
  "duration_count": 1
}
FieldRequirement
subscription_idMust be included in the credential and remain issuable under the reseller's live application grant.
customer_reference1 to 160 safe characters. Use a high-entropy opaque order ID; Konvoa persists only a domain-separated keyed digest.
duration_unitday, week, month, year, or lifetime. Custom dates and expires_at are not accepted by this API.
duration_countMust be 1. Each delivery consumes one allocated key from the selected duration bucket and issues one license with that preset.
maskOptional key template, at most 64 characters, using X, A, 9, and hyphens with at least 64 bits of randomness.

Idempotency-Key is required, 16 to 128 characters, and accepts ASCII letters, numbers, periods, underscores, colons, and hyphens. Use one stable value per customer order. Its namespace belongs to the reseller and application, so retrying the same order after replacing or revoking an API credential returns the original delivery without consuming a second balance unit. Changing the normalized request under that key returns HTTP 409. After the customer redeems the license, a replay does not expose it again and never creates a duplicate.

A new delivery returns HTTP 201. An eligible idempotent replay returns HTTP 200. The response includes data.delivery_id, replayed, the application and subscription IDs, the customer reference, and license. Read the raw key from license.key only when license.key_available is true, then deliver it through your own protected order flow.

Authorization
Konvoa rechecks the credential, reseller status, application grant, licenses:deliver scope, subscription allowlist, and current permissions on every call. Available balance is checked for every new delivery; an eligible idempotent replay neither consumes nor requires another balance unit.
Atomic delivery
Balance consumption, license creation, the immutable delivery journal, and its reseller/application idempotency alias commit in one transaction.
Errors
Invalid, expired, and revoked credentials share a generic HTTP 401 response. Disallowed scope or subscription uses a generic HTTP 403 response.
Transport
Responses use private no-store caching. Limits are 120 requests per minute per IP and 60 per minute per credential, with rate-limit and retry headers.
Rotate without an outage

Create and securely install the replacement credential, switch the reseller service, retry any queued customer orders with their original idempotency keys, then revoke the old credential. Those retries remain bound to the original deliveries across credential rotation. Use a new idempotency key only for a genuinely new customer order. Never embed a reseller secret in a desktop app, browser bundle, webhook payload, log, or customer email.

Account and billing

Manage identity, active sessions, and plan limits together

Account settings include profile details, password rotation, and a list of browser sessions with device, approximate network, creation, activity, and expiry context. An active console session renews near its rolling expiry only after its signed cookie and persistent session record both validate, while an absolute lifetime cap still requires a fresh sign-in. Renewal preserves the session nonce so the current page's CSRF binding remains valid. Revoke a selected session or all other sessions when access may have been exposed. Changing the password requires reauthentication.

Free, Starter, and Pro differ by resource limits. Paid checkout, invoices, plan changes, and cancellation use the billing portal when the payment provider is configured. A plan status must remain eligible for paid limits; an old plan name alone is not an entitlement.

Billing webhooks

Provider events update billing state, not client authority directly

The billing endpoint is service infrastructure, not a customer integration endpoint. It verifies the payment provider's signature against the exact raw request body, binds events to known checkout and subscription records, and processes repeated deliveries idempotently. Never expose the webhook secret to a browser, desktop client, reseller, or customer.

Operational requirement

Configure the production signing secret, use the exact public HTTPS endpoint registered with the provider, monitor rejected events, and reconcile subscription state before relying on paid limits.

Platform administration

Operator controls are distinct from customer workspaces

/admin is the platform operator surface for commercial accounts, sales and billing visibility, manual plan grants, platform event review, and service-wide management. /legacy/admin remains the isolated legacy-auth operator surface during migration. Neither surface is a public API or a substitute for owner and reseller scoping.

Operator actions should use named administrator identities, short sessions, strong authentication, audit review, and the smallest necessary access. Manual plan grants need an owner, reason, duration, and revocation path.

Production security

Ship only after the external controls match the code

Transport

Use end-to-end verified TLS, including Full (strict) between the edge and origin. Client signatures complement TLS; they do not replace it.

Secrets

Keep database, signing, payment, provider, and session secrets outside source and release archives. Rotate them with a tested overlap or invalidation plan.

Client release

Pin response keys, sign the executable with the platform mechanism, compute the shipped artifact digest, and verify external updates before staging.

Browser surfaces

Keep same-origin and CSRF checks, secure cookies, bounded inputs, private cache headers, and crawler exclusion on authenticated pages.

Operations

Monitor authentication failures, policy denials, reseller issuance, plan changes, key reveals, revocation, and provider health without logging credentials.

Recovery

Test encrypted backups, database integrity, rollback, response-key rotation, provider outage behavior, and forced session revocation.

LICENSE_KEY_ENCRYPTION_SECRET protects unused license ciphertext and is also the root for separate keyed customer-reference and idempotency digests. Konvoa pins a non-secret fingerprint of that root and fails reseller delivery closed if it changes. Rotate it only through a coordinated maintenance migration that re-encrypts ciphertext, migrates blind indexes, and updates the pinned fingerprint; changing it in place makes existing unused ciphertext and historical reseller idempotency lookups unavailable.

Read the security architecture for the threat boundary and the developer guide before changing any canonical request, token, response, or download-verification behavior.