LayerVBack to the live demo

Deploy LayerV to protect your site or resource

Deploy LayerV for an application you control: a site, investor portal, internal dashboard or HTTP service. This fresh-install guide takes a human or AI agent from LayerV account signup to a protected resource with verified temporary access, using Docker on Linux. Everhaven is only a demonstration; you do not need its branding, fictional report, hosting account or demo-session endpoint.

Version 1.5 · Technical baseline verified: 18 September 2026. The tested versions are Linux/amd64, qURL CLI v2.6.0 and qurl-go v0.17.0. This guide describes that tested native-enrollment path. Check current release documentation before upgrading; do not mix native state with generated headless or externally supervised enrollment instructions.

Full Markdown · Docker starter · Agent checklist · Try the reference demo

On this page

1. Sign up for LayerV and create an API key

Visit layerv.ai, choose Get started, and sign up for a LayerV account. Complete any account verification requested. In your LayerV dashboard, open API Keys, choose Create API Key, give it a recognizable name, select the required scopes, and choose Create Key. Save the API key in your secret manager when it is shown; do not put it in this guide, source control or browser code.

For the qURL CLI enrollment in this guide, select qurl:agent and qurl:resolve. You will enter this API key at the CLI's hidden prompt in step 4. In step 5, create a separate API key with qurl:read and qurl:write for your backend to create qURLs. Separating these purposes lets you revoke the API key used only for enrollment after verifying the device credential works.

LayerV terms used in this guide

Term Meaning here
Protected resource The application you want to make accessible through LayerV
qURL CLI The qurl command-line tool used to enroll, publish and share a protected resource
qURL Connector The component that connects your private application to LayerV; this guide runs it through the qURL CLI
CRID The protected resource identifier returned by qurl publish; use the actual value returned by LayerV
qURL A temporary access link for a protected resource
API key An account credential with selected scopes; used here for enrollment or by your backend to create qURLs
Enrollment token A single-use credential for enrolling an agent or connector; a separate credential type used by other enrollment workflows
Device credential The restricted credential stored locally after enrollment and reused when the qURL Connector restarts
Scopes The permissions assigned to a credential
Session duration How long access lasts after a qURL is redeemed; separate from the qURL's expiry

The API key entered during this guide's native CLI enrollment is not itself an enrollment token. See LayerV credentials and the qURL CLI documentation for the supported workflows.

2. Plan a fresh installation

You will deploy a new protected resource on a Linux host. Its application listens on loopback inside a Docker network namespace. The qURL Connector shares that namespace and connects outbound to LayerV, so the application needs no public inbound web port. Your backend creates qURLs only for callers it has authorized.

Use this recipe for an HTTP dashboard, internal tool or API that you own or are authorized to operate. The downloadable starter serves a harmless static page. For a dynamic application, use its container image and configure its listener on the same loopback address.

How access works

Create a qURL
Person → sign-in → your backend checks permission
       → LayerV API creates a qURL → person receives the qURL

Access the protected resource
Browser with redeemed qURL → LayerV gateway
                          → qURL Connector → loopback-only application

The gateway handles access through LayerV. The origin is the server running your application. Keeping the application off public inbound ports reduces its exposure to direct requests. It does not make every service on the host, such as administrative SSH, disappear or make the application unhackable. The application must still enforce permissions on its own data and actions. A qURL's expiry controls when it can be redeemed; session duration controls subsequent access. Expiry does not remove data already downloaded by a browser.

Everhaven demonstrates a fictional real estate company giving Maya, an approved investor, temporary access to a Harbor House investment report. Its public page simulates completed sign-in and investment approval. A signed demo cookie is not real authentication. For your application, authenticate the actual caller and authorize their specific resource before creating a qURL. Keep private reports and downloadable data exclusively on the protected application; do not include them in a public frontend build or storage bucket.

Agent deployment instructions and the deployment checklist describe how to deploy LayerV for your own resource. The LLM index discovers these instructions. An optional reference-demo walkthrough appears after the deployment instructions; trying Everhaven is not a deployment step.

Where to use this

An investor needs a private investment report. An automation needs a private service to perform an approved task. In both cases, a trusted backend checks who is asking and which protected resource they may use, then creates temporary access. The application stays private while its authorized callers change.

For an AI workflow, a gateway you operate can keep a provider API key in its own secret manager and make approved provider calls. LayerV can protect access to that gateway. LayerV does not itself remove a provider's authentication requirements, inspect every prompt or authorize individual model operations. Your gateway must implement those controls. This guide verifies the browser dashboard path; an automated client must use a supported LayerV client flow and be tested separately. A TypeScript wrapper, automatic renewal or identity-provider integration is not supplied by this starter.

What runs where, and how often

Component Location and lifecycle
Application and qURL Connector Installed once on your Linux host; remain running between visits. The connector initiates outbound connectivity.
Authorization backend Runs where callers can reach it before admission. On each request it verifies identity, checks permission, and uses the LayerV SDK to create a qURL.
Browser or supported automated client Receives temporary access to the authorized resource. It receives neither your LayerV API key nor a provider API key kept inside your gateway.
Persistent connector identity Stored in the protected state directory. Reused on restart; not recreated for each link or user.
qURL and admitted session Have separate lifetimes. A new access request can produce a new qURL without reinstalling the connector. Renewal must repeat authorization.

A protected resource can be an entire web application with multiple routes and assets; a qURL is not a substitute for the application's per-user, per-record or per-operation permissions. A CRID identifies a resource. It is not a replacement for a caller's authorization or proof that a client is trustworthy.

Record these inputs for your new installation:

Input Decision to record
Host Linux machine, CPU architecture, Docker access and administrative recovery access
Application Container image, loopback port, health endpoint, storage and dependencies
LayerV account Intended owner and environment; this guide uses production
Connector ID A new, stable identifier for this installation, such as private-dashboard
Authorization Which authenticated users may access which protected resource
qURL policy qURL expiry, session duration, and use and session limits
Network Outbound connectivity, IPv4/IPv6 configuration and restricted management access

DigitalOcean example: create an Ubuntu Linux droplet sized for your application, install Docker Engine and the Compose plugin, and keep console access available. Permit administration only through your approved network or management system. Do not add public inbound rules for the application's HTTP port or configure a public load balancer for it. The same pattern works on another Linux provider; DigitalOcean is an example, not a requirement.

Follow the official Docker installation instructions. Docker-published ports can bypass some host firewall expectations, so this recipe publishes none. Confirm outbound DNS and the LayerV connections required by your deployment, including HTTPS and NHP UDP 443. Consult the current connector requirements for restricted-egress networks.

3. Install the application and qURL CLI

Download and inspect starter.zip, then extract it into a new directory on the chosen host. The archive contains a starter/ directory plus this guide and checklist. Run the commands below from starter/, as a trusted operator with permission to administer Docker. Docker daemon access is privileged.

The starter uses two services: origin and connector. It has no ports: mappings, no host networking, and no Docker socket mount. Nginx listens on 127.0.0.1:8080 inside the shared namespace. The connector uses network_mode: service:origin to reach it. Both processes run as non-root users with read-only root filesystems, dropped capabilities and restricted writable directories.

Choose an approved Nginx image, pull it and pin its resolved digest. The following selects the official Alpine image; review and scan the resulting image according to your policy before using real data. The connector image is already pinned to the exact qurl v2.6.0 digest used by the reference deployment.

docker pull nginx:alpine
docker image inspect nginx:alpine --format '{{index .RepoDigests 0}}'

Create a local .env file containing only non-secret configuration, replacing the digest placeholder with the actual output:

ORIGIN_IMAGE=nginx@sha256:REPLACE_WITH_VERIFIED_DIGEST
CONNECTOR_ID=private-dashboard

Choose a fresh connector ID for a distinct resource. Do not reuse an existing installation's identity or copy its state files. For another CPU architecture, confirm that the selected images support it; the reference was tested on Linux/amd64.

Prepare the persistent identity directory and start only the origin:

sudo install -d -m 0700 -o 65532 -g 65532 state
docker compose config --quiet
docker compose up -d origin
docker compose exec -T origin wget -q -O - http://127.0.0.1:8080/healthz

Expected health response: private-resource-ok. For a real app, replace this with a harmless endpoint that returns a distinctive response. Never put account details, secrets or customer data in a health response.

Keep the app and connector in the same network namespace. If replacing Nginx, adapt the user ID, writable volumes, health check and content security policy to that application. The starter's form-action 'none' suits its static page, not a login form. Test forms, redirects, secure cookies, APIs and any WebSockets your application needs. Do not remove controls wholesale to make a failing feature work.

4. Enroll the qURL Connector and publish the protected resource

Use the API key created in step 1 with the qurl:agent and qurl:resolve scopes. Run the qURL CLI login command:

docker compose run --rm --no-deps connector login

Paste the API key only at qURL API key (input hidden). Do not put it in command arguments, Compose, source files, screenshots or logs. An automation can supply it through a protected secret-manager pipe to standard input; never write an echo command containing a literal key.

Native enrollment stores a restricted device credential in state/. The CLI uses the API key for enrollment and does not retain it as the runtime credential. The API key remains valid until explicitly revoked; enrollment does not automatically revoke it. Keep the directory owner-only and back it up securely. This recipe uses native plaintext identity storage protected by filesystem permissions; it is not encrypted-at-rest state. If your policy requires sealed state, use LayerV's separate supervised deployment workflow.

Start the connector:

docker compose up -d connector
docker compose logs --tail=30 connector

Wait for Published, Status: serving, and a genuine CRID. Save the full CRID in your server's configuration. Never substitute a resource public key, hostname, example string or made-up identifier. Use the production endpoint and production identity together. If deployment settings are missing, obtain the official deployment configuration from LayerV; do not invent trust keys or silently switch environments.

Use the actual CRID below:

docker compose exec -T connector /usr/local/bin/qurl whoami
docker compose exec -T connector /usr/local/bin/qurl inspect YOUR_CRID -o json

Confirm the owner, connection_state: serving, daemon_state: serving and healthy local target. A container being “Up” is not enough. The CLI's native publish --foreground behavior keeps serving in the container process while Docker supervises it.

5. Create qURLs from an authorized backend

For an operator-only smoke test, the enrolled qURL CLI can create a qURL:

docker compose exec -T connector /usr/local/bin/qurl share YOUR_CRID --ttl 60s

Open the returned link in a browser. Treat it as a credential and avoid copying it into public logs or tickets. This CLI command requests an opening lifetime; it does not configure the exact single-use/two-minute-session policy below. Use the SDK for those explicit limits.

For your backend, open API Keys → Create API Key in the same production LayerV account. Create a separate API key with only qurl:read and qurl:write; leave qurl:agent and qurl:resolve disabled. Store this API key in your backend's secret manager as LAYERV_API_KEY. Store the CRID separately as non-secret configuration. Do not send the API key to frontend JavaScript.

The following Go SDK example creates a qURL with a one-minute expiry, a two-minute session duration, single use and a maximum of one session. It belongs after your server's authentication and permission checks, not in an anonymous endpoint. It requires context, errors, os, time and github.com/layervai/qurl-go/qurl; the reference pins the module to v0.17.0.

apiKey := os.Getenv("LAYERV_API_KEY")
if apiKey == "" || authorizedCRID == "" {
    return errors.New("access service is not configured")
}
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
client, err := qurl.NewClient(qurl.BearerToken(apiKey))
if err != nil {
    // Return a generic failure; do not issue access or log credentials.
    return err
}
portal, err := client.ResourceByCRID(authorizedCRID).CreatePortal(ctx,
    qurl.ValidFor(time.Minute),
    qurl.WithSessionDuration(2*time.Minute),
    qurl.OneTimeUse(),
    qurl.MaxSessions(1),
    qurl.WithLabel("Private application access"),
)
if err != nil {
    return err
}
// Return portal.Link only to the authorized caller, with Cache-Control: no-store.

authorizedCRID must come from trusted server-side policy. Do not accept an arbitrary CRID, target URL, tenant or duration from the browser and create a qURL without checking permission. Bound the API request with a timeout and return sanitized errors.

Run the complete SDK example

The starter includes access-link/main.go, go.mod and go.sum: a complete operator-run Go program using the same SDK limits. It creates one real qURL for the configured CRID. It is not a public web endpoint and supplies no user-authentication system.

On a trusted backend or operator machine with Go 1.25.13 or later, supply LAYERV_API_KEY from your secret manager and LAYERV_RESOURCE_CRID from step 4. Do not paste a literal API key into shell history. From the extracted starter/ directory:

cd access-link
go mod download
umask 077
go run . > access-link.json

The JSON contains a temporary credential in qurl_link, an expires_at timestamp and session_seconds. Open the link in a browser before expiry. Keep this file private and delete it after testing; do not put it in logs, source control or a public report. A plain HTTP request to the qURL opening page does not perform its browser admission flow. For automated retrieval use a currently supported LayerV client flow; the reference browser demo does not imply arbitrary HTTP clients can redeem qURLs.

For a real application, put the SDK operation behind the verified identity and permission checks below. The browser receives the link; the server keeps the API key and selects the allowed CRID. The operator program is a runnable starting point for that operation, not a replacement for those checks.

Authorization pattern, with Auth0 as an option

This is a framework-neutral design, not a ready-made authentication library:

POST /access-links
  1. Validate the existing session or API access token on the server.
  2. Apply CSRF protection when using browser cookies.
  3. Resolve the user's tenant and requested application from trusted policy.
  4. Deny unless this user may access that specific resource.
  5. Apply per-user and per-resource rate limits and an audit policy.
  6. Create a qURL with explicit limits for the server-selected CRID.
  7. Return the link privately with no-store; never log the full link or key.

With Auth0, use its supported server middleware to validate the access token's signature, issuer, intended audience, expiry and required permissions. An ID token or a decoded-but-unverified JWT is not authorization for your API. Follow Auth0's token validation guidance.

Put sign-in and the backend endpoint that creates qURLs somewhere the user can reach before admission. Your application can keep its own login and authorization behind LayerV, but obtaining a qURL cannot depend solely on a login page that the user cannot yet reach. Auth0's hosted login remains a separate service. Configure callbacks and cookies for the actual access hostname; temporary hostnames may require additional application design. This guide does not claim every existing login flow works unchanged.

A bearer qURL can be forwarded to another person before use. Single use limits redemption; it does not prove the holder's identity. Keep application-level authorization for sensitive actions and use supported recipient-binding controls if your threat model requires them.

6. Verify the protected resource

Before admitting real users, confirm that the new application has no public container port mapping, public load balancer, public hosting URL or other direct route that bypasses LayerV. Check both IPv4 and IPv6 where enabled. Keep administrative access restricted and separate from application access.

Check the deployment itself:

docker compose ps
docker inspect "$(docker compose ps -q origin)" --format '{{json .HostConfig.PortBindings}}'
docker compose exec -T origin wget -q -O - http://127.0.0.1:8080/healthz

There must be no published host bindings for the application. Image metadata such as 80/tcp alone is not a host port mapping; a host binding such as 0.0.0.0:8080->8080/tcp is.

From an authorized test machine outside the host, test the known origin IP, application ports and IPv6 routes if present. Use only targets you own or have permission to test. Record the actual result: timeout, refusal, TLS error or HTTP status. An HTTP 401 still means an endpoint responded; refusal means the host responded with a closed port. Neither is “nothing exists.” DNS absence, a timeout by itself and a successful local health check are not sufficient proof of the whole access boundary.

Acceptance tests

Test Required evidence
Issuance policy Missing, forged or expired identity credentials and unauthorized resource requests are denied before any LayerV create-qURL call
Before admission No direct route serves the private app; test an unadmitted client against the real protected route where available
Authorized visit A newly issued qURL opens the expected application and a fresh no-cache health request returns its distinctive response
Reuse Reopening the same single-use link cannot establish a new session; record denial without assuming its cause from generic text
After session expiry With the app still healthy, a fresh request through the admitted route no longer returns protected content
Separate caller A caller that has not redeemed the link does not inherit access; use a separate network where admission is IP-bound, not just an incognito tab
Restart Connector returns to serving with the same CRID and protected origin; device state survives
Public exposure No application host binding, public load balancer, hosting URL or IPv4/IPv6 route bypasses LayerV

For an in-browser health check, request /healthz with a unique query parameter, cache: 'no-store', redirect: 'error', a timeout and an exact expected body. Do not treat a cached page, a countdown, a login redirect or the LayerV dashboard's use count as evidence of continuing origin access. Check expiry without restarting the connector during that test, so a restart does not explain the failure.

Record both successful access during the session and the actual failed request after its configured duration while the application remains healthy. Record UTC timestamps, vantage point, expected response and observed network/HTTP outcome. Keep operator verification separate from checks made by each visitor. An automatic timer may trigger a fresh request, but must never manufacture an expired or denied result. Passing those checks demonstrates the tested behavior; it does not establish that every possible attack path has been audited.

7. Finish enrollment and operate it

Once access works, restart the connector and confirm it still uses the same CRID:

docker compose restart connector
docker compose exec -T connector /usr/local/bin/qurl inspect YOUR_CRID -o json

Allow it to move from starting to serving before judging the result. In LayerV API Keys, revoke only the API key used for enrollment, then repeat the restart and new-qURL checks. Keep the device credential and the separate API key used by your backend active. Revoking an API key is not the same as revoking the device credential.

To take this container's route offline, stop its foreground process:

docker compose stop connector

This is a runtime stop, not an assertion that individual qURLs or all issued grants were revoked. Use the supported LayerV revocation controls when the intent is to revoke access, and verify the result with a fresh request. To resume this deployment, start its connector and confirm readiness again.

Back up the complete identity directory securely, never commit it, and never run independent installations against copies of the same live identity. If you recreate the origin container, recreate the connector too so it joins the correct network namespace. Record the installed image digests and configuration for maintenance. Keep the application private during recovery. Test upgrades before replacing a working binary or state format.

8. Known limits and troubleshooting

These observations describe the reference deployment on 18 September 2026. Recheck them against the current product before treating them as permanent limitations.

Symptom What to check
Origin health fails Application process, loopback listener, file permissions and health path; do not publish a port to fix it
Enrollment fails Correct account/environment, required scopes, supported outbound network, state ownership and official deployment settings
Connector starts but is not serving Inspect connection/daemon/target health; container uptime alone is insufficient
State permissions or wrong-owner error Preserve the complete directory; do not delete individual identity files or reuse another account's state
Link opens but app fails Relative assets, redirects, cookies, CSP, form actions, callbacks and supported protocols
Use count remains zero The production reporting issue was reproduced; verify fresh origin access independently
Domain has TLS but cannot bind Native connector custom-domain assignment was rejected in the tested production environment

Custom domains are outside this guide's working path. The reference uses qurl.site; its attempted custom domain remains unbound. Do not expose the origin to obtain a branded hostname, and do not treat an unused hostname timing out as proof of protection.

9. Reuse this guide

The starter contains no Everhaven account key, resource ID, host address or private data. It is a reference implementation for a static origin, not a universal drop-in configuration for every application. The generic starter was adapted from the working deployment and checked for configuration structure; it has not been independently deployed into every target environment. Run the acceptance checks on your own host and account. Keep the private application and the backend that creates qURLs separate, adapt the application deliberately, and record the evidence before calling a deployment protected.

For AI-assisted work, use the agent checklist. It defines the inputs to collect, steps to execute, conditions to stop on and evidence to return. A document grants no permission to modify an account or host; use the operator's actual authorization and deployment scope.

Sources and reference implementation

Keep the version, verification date and limitations with this guide when adapting it for another documentation site.