Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/org-quicko/linq/llms.txt

Use this file to discover all available pages before exploring further.

The linq Client UI is a static Next.js export — plain HTML, CSS, and JavaScript with no server-side runtime. It connects to a linq API server at runtime through a form in the browser, so no server URL is compiled into the build. One deployed UI can administer any number of linq instances, and you can add or switch servers without touching the deployment. Because the UI is purely a client, it can live anywhere: bundled into the same container as the API, or on a completely separate host.

Two deployment shapes

In the combined shape, the linq server builds and serves the Client UI from the same process. This is what the published Docker image and all eight main Compose stacks use by default.Build command (from the repo root):
bun run build:client
This writes apps/client/out, compiled for the path set in LINQ_CLIENT_BASE_PATH (default /home). The server mounts this directory and serves it at that path on every host it answers.How the server mounts it:The static export is served by the server’s mountAdmin handler. With LINQ_APP_HOST unset, the UI is available at http://<any-host>:<port>/home/ (or whatever LINQ_CLIENT_BASE_PATH is set to). With LINQ_APP_HOST set, the handler restricts the UI to that one host only, letting the base path be / without reserving any slugs on shortening domains.In Docker, the Compose examples pass LINQ_CLIENT_BASE_PATH to the build stage. Because the path is baked into the static files during docker build, changing it requires a rebuild:
docker compose -f docker/examples/docker-compose/01-bundled-postgres.yml up --build -d
The combined image build uses two stages: the first builds apps/client/out; the second copies that output into the runtime image alongside the server sources. The Bun runtime executes the server directly — there is no server bundle step.

Adding a server at runtime

Regardless of which shape you deploy, the UI opens with a form to add a linq server. No configuration file is needed — everything is stored in the browser’s localStorage.
FieldWhat to enter
NameAny label, e.g. production or staging
Server URLThe base URL of your linq API, e.g. https://linq.example.com
API keyA linq_… key from your instance’s Keys page or server logs
The UI validates the key against the server before saving. If the URL is unreachable or the key is invalid, you get an error immediately rather than a blank page later. Once saved, the server appears in the sidebar. You can add multiple servers and switch between them at the foot of the sidebar — one UI instance can manage any number of linq deployments, and the people using it need no access to the deployment to point it at a new server.
Keys are created on the Keys page (/home/keys) or via the API (POST /api/v1/keys). On an instance with no keys, linq mints and prints an admin key at boot. Copy it then — only its hash is stored and it cannot be read back.

Docker images

Three Dockerfiles cover the container shapes for the Client UI:

Dockerfile (published image)

docker/dockerfiles/Dockerfile — builds the API, redirects, and Client UI in one image. The UI is mounted at LINQ_CLIENT_BASE_PATH (default /, requires LINQ_APP_HOST at runtime). This is ghcr.io/org-quicko/linq / labsatquicko/linq.

Dockerfile.server

docker/examples/dockerfiles/Dockerfile.server — API and redirects only, no Client UI build stage. The server returns 404 on the configured Client UI path and continues serving the API and redirects. Used by 09-server-only.yml.

Dockerfile.client

docker/examples/dockerfiles/Dockerfile.client — standalone Client UI only. Runs serve-static.ts under plain Bun; no nginx, no Postgres, no environment variables needed. Used by 10-client-only.yml.

10-client-only.yml

docker/examples/docker-compose/10-client-only.yml — Compose file for the standalone client container. The client’s published host port is ${LINQ_CLIENT_PORT:-3001}. Works standalone or layered on top of a server Compose file.
Running the API and standalone client side by side:
docker compose \
  --env-file .env \
  -f docker/examples/docker-compose/01-bundled-postgres.yml \
  -f docker/examples/docker-compose/09-server-only.yml \
  -f docker/examples/docker-compose/10-client-only.yml \
  up --build -d
With LINQ_PORT=8080 and LINQ_CLIENT_PORT=8081 in .env, the API is on port 8080 and the standalone UI is on port 8081. Both containers listen on 3000 internally — each has its own network namespace, so there is no conflict.

LINQ_APP_HOST mode

When LINQ_APP_HOST is set, the UI answers only on that specific host, at /. Every other host continues serving short links normally. This lets you give the UI a clean root URL (https://linq.example.com/) while keeping https://link.example.com/<slug> for short links — from a single process and container.
LINQ_APP_HOST=linq.example.com       # UI at /, API at /api/* on this host only
LINQ_DEFAULT_DOMAIN=link.example.com # short links on this host
This mode requires the published linq image (built with LINQ_CLIENT_BASE_PATH=/) — or any image rebuilt with that same build arg. The app host must differ from LINQ_DEFAULT_DOMAIN and from every registered domain; linq refuses to start otherwise. Behind a reverse proxy, route both hostnames to the same linq port and preserve the Host header — that is how linq distinguishes them. With Caddy sync enabled, linq pushes the app host’s route at boot automatically; see Automatic HTTPS with Caddy.
Archive any existing domain row whose host equals the intended LINQ_APP_HOST before setting the variable. The UI claims every path on that host, so any links previously registered under it will stop resolving.

Cross-origin API calls

The linq server enables CORS on all /api/* routes, accepting calls from any origin. The API key is the only thing that authorises a request; no cookie is ever involved. This means:
  • A standalone UI on https://app.example.com can call an API on https://linq.example.com without any proxy.
  • The development UI on port 3001 calls the API on port 3000 across origins — exactly as a deployed standalone UI would.
  • If you see requests fail with an opaque network error, suspect CORS configuration on the server, not a missing proxy.
No API URL is compiled into either UI shape. The combined shape compiles only its base path into the static files; the standalone shape always uses /. Adding a server URL is a runtime step done in the browser.

Build docs developers (and LLMs) love