An enrollment token is minted
You mint a one-shot enrollment token bound to this connector id and stage it as a read-only file for first registration.
Connector setup guide
The Enterprise connector lets approved people, apps, and agents access an existing app or API without exposing it to the public internet.
The CLI is simpler for local development, demos, and single-machine services.
Deploying the standalone connector beside a service? LayerV generates its configuration for your account. Contact LayerV to set one up.
Operate
You mint a one-shot enrollment token bound to this connector id and stage it as a read-only file for first registration.
On first start, the qURL CLI presents the enrollment token, registers its persistent NHP keypair, and stores the device credential the platform mints under the state directory at /var/lib/qurl.
The enrollment token is consumed by that first success — remove the token file. Future restarts authenticate with the device credential in the mounted state directory, and fail closed if that state is missing.
The connector knocks through NHP and receives the public address at runtime; customers do not configure internal LayerV hosts.
The connector emits a structured record for each bootstrap, knock, login, proxy, and teardown decision. It is on by default and mirrored into your runtime logs; route it to a JSONL file when you need forensic retention.
The default file provider stores the connector private key in the state directory at mode 0600. Choose aws-kms or gcp-kms when durable state must move without plaintext key files; state then holds private_key.sealed.json instead.
Standard connector onboarding stays simple. AWS Nitro and GCP Confidential Space are an optional advanced tier, for customers whose security owner requires cloud attestation before KMS releases the connector private key.
# AWS KMS
-e LAYERV_KEY_PROVIDER=aws-kms \
-e LAYERV_AWS_KMS_KEY_ID=arn:aws:kms:us-east-1:111122223333:key/1234abcd-... \
-e LAYERV_AWS_KMS_REGION=us-east-1
# GCP Cloud KMS
-e LAYERV_KEY_PROVIDER=gcp-kms \
-e LAYERV_GCP_KMS_KEY_NAME=projects/acme-prod/locations/us/keyRings/qurl/cryptoKeys/agent-identity
# AWS Nitro attested KMS release
-e LAYERV_KEY_PROVIDER=aws-nitro \
-e LAYERV_AWS_KMS_KEY_ID=arn:aws:kms:us-east-1:111122223333:key/1234abcd-... \
-e LAYERV_AWS_KMS_REGION=us-east-1 \
-e LAYERV_AWS_NITRO_ATTESTATION_DOCUMENT_FILE=/run/qurl/attestation.cose \
-e LAYERV_AWS_NITRO_ATTESTATION_DOCUMENT_ENCODING=raw \
-e LAYERV_AWS_NITRO_RECIPIENT_UNWRAP_COMMAND=/opt/qurl/bin/unwrap-recipient
# GCP Confidential Space attested KMS release
-e LAYERV_KEY_PROVIDER=gcp-confidential-space \
-e LAYERV_GCP_KMS_KEY_NAME=projects/acme-prod/locations/us/keyRings/qurl/cryptoKeys/agent-identity \
-e LAYERV_GCP_CONFIDENTIAL_SPACE_TOKEN_FILE=/run/container_launcher/attestation_verifier_claims_token \
-e LAYERV_GCP_CONFIDENTIAL_SPACE_IMAGE_DIGEST=sha256:... \
-e LAYERV_GCP_CONFIDENTIAL_SPACE_SERVICE_ACCOUNT=qurl-agent@acme-prod.iam.gserviceaccount.com
# Optional sealed-provider runtime placement. Use tmpfs.
-e LAYERV_NHP_RUNTIME_DIR=/dev/shm
# Existing file-provider volume migration, one restart only:
-e LAYERV_ALLOW_KEY_MIGRATION=true/dev/shm available, or set LAYERV_NHP_RUNTIME_DIR to another tmpfs-backed path. Do not use disk-backed runtime dirs in production.LAYERV_ALLOW_KEY_MIGRATION=true for the migration restart only, then remove it after private_key.sealed.json is written.Connectors tag — GET /v1/connectors/installations — lists third-party integrations (Slack and others), not Connector sidecars. The dashboard shows them under Integrations.GET and PUT /v1/resources/{id}/sharing report lifecycle status only — desired_state on|off, serving_epoch, and connection_state stopped|connecting|serving — not configuration.When a call fails, the error catalog lists the code, status, cause, and fix.
Reference — Resource types
qURL protects three kinds of resource, and only one of them is served by a Connector. The type decides how access is granted and which minting options apply.
| Type | Meaning | How it is reached |
|---|---|---|
url | a target URL | The default resource, reached through qURL’s reverse proxy. Works for browsers and agents. Not a Connector resource. |
tunnel | an Enterprise connector | A resource served by a Connector, reached through the Connector session (NHP-gated), so the origin needs no inbound port. Supports target_path when minting a link, so a link can point at a path or query on the resource host. |
transit | content uploaded through an integration | Connector-owned uploads. Not listed via /v1/resources; use connector installation operations for their reports. |
Only type=tunnel accepts target_path when minting a link.
Reference — Lifecycle
GET /v1/resources/{id}/sharing, and PUT to the same path to change the desired state, report lifecycle status only. They never return configuration.
| Field | Values | Meaning |
|---|---|---|
resource_id | string | The resource key retained for compatibility. |
crid | string | The permanent, location-independent identifier. |
desired_state | on | off | The lifecycle state you asked for. |
serving_epoch | int64 | Rotates whenever the lifecycle state changes. |
connection_state | stopped | connecting | serving | serving requires at least one authenticated Connector connection that is ready to carry traffic. |
Repeating the same desired state is idempotent and preserves serving_epoch. Changing state rotates the epoch, so connections from the previous lifecycle stop serving.
The Connector presents its enrollment token and the platform mints the device credential. That durable identity is what every restart reuses.
Enrollment binds the Connector resource to that identity.
PUT /v1/resources/{id}/sharing with desired_state: on.
GET the same path until connection_state is serving.
If both the durable state and the enrollment token are gone, the Connector will not silently register a second identity. It fails closed and must be enrolled again.
Reference — Credentials
Four credential kinds show up around a Connector. Each has a different origin and a different set of allowed operations.
| Credential | What it is | What it can do |
|---|---|---|
api_key | Account API key. Carries scopes. | With the qurl:agent scope, mints enrollment tokens (target: agent or connector). Its scopes decide what it may do. |
enrollment_token | A one-shot token bound to one target. | Enrolls exactly one target (agent or connector). Single-use. Cannot create credentials. |
| device credential | Created by the platform at enrollment. Durable. Never creatable through the API. | The machine’s durable identity and what every restart reuses. Appears in credential listings so it can be revoked. |
| JWT | The console and session credential (Auth0). | Some operations are JWT-only — Usage, Customer, and Billing tags, and PATCH /v1/api-keys/{key_id}. API keys cannot update API keys. |
Reference — Plans and limits
Verified limits that apply across plans.
| Limit | Value |
|---|---|
Access-link lifetime (expires_in / expires_at on link creation and extension) | Minimum 1 minute, maximum 30 days on all plans. |
| Enrollment tokens and child API keys | Maximum 24 hours (default 24 hours). |
GET /v1/quota rate_limits.max_expiry_seconds | 2592000 (30 days) on all customer plans. |
API-key plan limit (api_key_limit, 403) | free 3, growth 50. |
connector_disabled is a 403 meaning Connector operations are temporarily unavailable. It is an availability state, not an authorization failure.
Reference — Errors
Each API error carries a code, an HTTP status, a cause, and the fix.
| Code | HTTP | Meaning | Fix |
|---|---|---|---|
invalid_input | 400 | Mismatched fields for the requested kind (for example scopes on an enrollment token, or target/claims/expires_in on an API key, or more than one claim). | Send only the fields that kind accepts. |
invalid_session_duration | 400 | The requested session duration is not accepted. | Use a valid duration or omit it. |
bootstrap_key_consumed | 409 | The enrollment token was already consumed by another enrollment. It is terminal. | Mint a new token. Never retry the same one concurrently. |
slug_in_use | 409 | A Connector resource with that slug already exists. | Use find_or_create: true or choose another slug. |
resource_alias_mismatch | 409 | The alias does not match the existing resource. | Reuse the existing alias or choose a different one. |
resource_type_conflict | 409 | The existing resource has a different type. | Use the matching resource or a different identifier. |
connector_disabled | 403 | Connector operations are currently unavailable. | Retry later or verify Connector availability. |
account_frozen | 403 | The account is frozen. | Resolve the billing or account state. |
api_key_limit | 403 | Plan credential limit reached (free 3, growth 50). | Revoke an unused key or upgrade. |
knock_failed | 502 | Failed to grant network access; the token may have been consumed. | Mint a fresh link and retry. |
Reference — Naming
A few terms in the API and the console sound alike. These are the distinctions.
GET /v1/connectors/installations is a third-party integration (Slack, storage accounts) — a different thing. In the dashboard, sidecars are under Connectors and installations under Integrations.desired_state and connection_state on /v1/resources/{id}/sharing are lifecycle. POST /v1/resources/{id}/share mints an access link.resource_id is the resource key retained for compatibility. crid is the permanent, location-independent identifier — prefer it. slug is the stable Connector name. alias is a memorable handle.