Use the guided modal
Best for most admins. Slack asks for the ID, optional alias, local port, target runtime, and Docker or Compose service/container name.
/qurl-admin protect-connectorDeveloper guide
Use Slack to mint the qURL Connector and one-time bootstrap key, then use this guide to understand exactly what to run, what to keep, and what to delete after the sidecar connects.
Runs /qurl-admin protect-connector and chooses the runtime.
Stores one-time key, config, and persistent agent state.
Run /qurl get $alias after the qURL Connector is connected.
Quick start
This path assumes a local HTTP service is already listening on port 8080 and that a Slack admin has access to the LayerV Slack app. Slack is where secrets are minted; this page is where operators can understand and verify the install.
Best for most admins. Slack asks for the ID, optional alias, local port, target runtime, and Docker or Compose service/container name.
/qurl-admin protect-connectorBest for repeatable setup, sandbox work, or teams that already know the target shape. Prefixing the ID with $ is accepted and normalized.
/qurl-admin protect-connector prod-dashboard env:docker port:8080 container:webThe guided modal is the easiest path. The typed command accepts the same customer-facing inputs when you want repeatable setup.
/qurl-admin protect-connector <connector-id|$connector-id> [env:docker|docker-compose|ecs-fargate|kubernetes] [port:8080] [alias:$alias] [container:<name>|service:<name>]Use one ID per service, such as prod-dashboard. Use the same ID for replicas of that same service; choose a different ID for a different service.
/qurl-admin protect-connector prod-dashboard env:docker port:8080 container:webFor Docker and Docker Compose, paste the whole Slack-generated block into a trusted shell on the host that runs the app. It writes the route config, creates the secret and state directories, prompts for the bootstrap key, and sets QURL_CONNECTOR_ID before starting the sidecar.
routes:
- type: http
local_ip: 127.0.0.1
local_port: 8080Watch the qURL Connector logs. Once the sidecar connects, delete the mounted bootstrap key. Keep the agent-state directory; future restarts use that identity.
docker logs -f qurl-connector-prod-dashboard
# After the logs show the qURL Connector connected:
sudo rm -f /run/secrets/qurl-connector/prod-dashboard/api_key
# Keep this directory. It stores the sidecar identity.
sudo ls /var/lib/layerv/qurl-connector/prod-dashboard/agentMental model
The bootstrap key is deliberately temporary. The durable sidecar identity lives in the mounted agent-state directory after the first successful start.
The Slack app creates or finds the qURL Connector for your account and ID, binds the channel alias, and mints a one-hour bootstrap key.
On first start, the sidecar uses the bootstrap key to register its persistent NHP keypair and cache the qURL Connector identity under the agent-state directory.
After the first successful connection, remove the bootstrap key. Future restarts use the mounted state directory and fail closed if that state is missing.
The YAML only names local routes. The sidecar knocks through NHP and receives the public qURL Connector address at runtime; customers do not configure internal LayerV hosts.
Concepts
These are the names that show up in Slack, Docker, and the dashboard. Once these are clear, the install flow becomes straightforward.
The stable identifier for one qURL Connector inside your account, such as prod-dashboard. The runtime sets it via the QURL_CONNECTOR_ID environment variable. Use the same ID for replicas of the same service. Do not reuse an ID for a different service.
The Slack handle users type later, such as $prod. If you leave it blank, Slack uses the ID. The alias can change; the ID should not.
A short-lived key used to register the sidecar on first start or after state recovery. It is not steady-state qURL Connector auth. Remove it after the first successful connection.
The persistent volume that stores the sidecar identity, agent_id, NHP peer config, and qURL Connector identity cache. The default file provider stores plaintext key material; KMS providers store a sealed blob. Treat backups as secret-bearing.
The qurl-proxy.yaml file tells the sidecar which local service to expose. qURL Connector identity comes from QURL_CONNECTOR_ID, so the generated route file intentionally omits name and server.addr; placement comes from bootstrap and the NHP ACK.
The deployment target, such as Docker, Compose, ECS/Fargate, or Kubernetes. It controls how the sidecar reaches localhost and where durable agent state lives.
Runtime guides
Slack tailors the generated block from this choice. The runtime decides how127.0.0.1 reaches your local service and where persistent agent state lives.
A web app already running in one Docker container on a Linux host.
/qurl-admin protect-connector prod-dashboard env:docker port:8080 container:webAn app already described by compose.yaml.
/qurl-admin protect-connector prod-dashboard env:docker-compose port:8080 service:webA task with awsvpc networking and durable storage for sidecar state.
/qurl-admin protect-connector prod-dashboard env:ecs-fargate port:8080A same-Pod sidecar in GKE, EKS, or another Kubernetes cluster.
/qurl-admin protect-connector prod-dashboard env:kubernetes port:8080Docker details
You should normally use the personalized Slack block. This abbreviated version shows the durable contract so it is clear why each mount and environment variable exists.
# The Slack-generated block is personalized for your qURL Connector ID and image.
# Replace vX.Y.Z with the immutable image tag shown in the Slack output.
# This is the important shape it follows.
QURL_CONNECTOR_ID='prod-dashboard'
WEB_CONTAINER='web'
CONNECTOR_CONTAINER="qurl-connector-${QURL_CONNECTOR_ID}"
SECRET_DIR="/run/secrets/qurl-connector/${QURL_CONNECTOR_ID}"
AGENT_STATE_DIR="/var/lib/layerv/qurl-connector/${QURL_CONNECTOR_ID}/agent"
CONFIG_FILE="$PWD/qurl-proxy-${QURL_CONNECTOR_ID}.yaml"
# The block prompts for the bootstrap key with hidden input.
# Do not paste the key into the shell script itself.
docker run -d \
--name "$CONNECTOR_CONTAINER" \
--network "container:${WEB_CONTAINER}" \
--restart=on-failure:5 \
-v "$AGENT_STATE_DIR:/var/lib/layerv/agent" \
-v "$SECRET_DIR:$SECRET_DIR:ro" \
-v "$CONFIG_FILE:/work/qurl-proxy.yaml:ro" \
-e QURL_API_KEY_FILE="$SECRET_DIR/api_key" \
-e QURL_CONNECTOR_ID="$QURL_CONNECTOR_ID" \
ghcr.io/layervai/qurl-connector:vX.Y.ZSecurity posture
The bootstrap key is needed for first registration and explicit state recovery. It is not the long-lived qURL Connector credential. The long-lived identity is the keypair under the mounted agent-state directory.
Standard qURL mode does not put LayerV internal routing addresses in customer YAML. The sidecar bootstraps, knocks, receives the public FRP host and port in the NHP ACK, and dials that address at runtime.
Do not paste the bootstrap key into a saved shell script. Paste it only when the generated block prompts, or put it in the runtime secret manager.
Do not delete the agent-state directory, volume, or PVC after first start. It stores the sidecar identity used for future restarts.
Do not share one agent-state volume across concurrently running sidecars. Replicas can share an ID, but each replica needs its own state.
Do not put connect.layerv, proxy.layerv, frps-*.internal, server.addr, or a public connection port in qurl-proxy.yaml for standard qURL Connector installs.
/qurl get $prod-dashboard and reach the service.Advanced key storage
The default file provider is the simplest install: it stores the sidecar private key in the mounted agent-state directory with mode 0600. Enterprise host-mode installs can choose aws-kms or gcp-kms so durable state contains private_key.sealed.json instead of plaintext key files.
Startup decrypts the sealed blob with cloud KMS, writes the plaintext runtime config required by OpenNHP under tmpfs, and removes that runtime directory on clean shutdown. Warm restarts need live KMS decrypt permission, so KMS and IAM reachability become part of the qURL Connector startup SLO.
# 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
# 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.aws-nitro or gcp-confidential-space.Standard qURL Connector onboarding stays simple. AWS Nitro and GCP Confidential Space are an optional upper tier for customers whose security owner requires cloud attestation before KMS releases the sidecar private key.
# 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/... \
-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.comTroubleshooting
Most failures are local setup issues: missing Docker names, missing config files, wrong directory ownership, or state/key mismatch after recovery.
Replace the placeholder with the Docker container name, Compose service name, or re-run the Slack command with container:web or service:web.
Stop and use your platform secret manager instead. The bootstrap key should not land in terminal scrollback, shell history, or scripts.
Make sure qurl-proxy.yaml exists before the bind mount. Docker creates missing host paths as directories, which makes the mounted config unreadable.
The runtime runs as UID/GID 65532. Create the state directory with mode 0700 and owner 65532:65532 before first start.
That is intentional fail-stop behavior. Pick a fresh ID or intentionally delete/recreate the qURL Connector instead of silently rolling over.
Run the Slack install flow again to mint a fresh bootstrap key. Without state or a bootstrap key, the client fails closed.
Next step
The website should never ask you to paste a live bootstrap key. Use Slack to mint the short-lived key and this guide to understand, verify, and operate the install.