@lamalibre/create-lamaste 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +118 -0
- package/README.md +114 -0
- package/bin/create-lamaste.js +15 -0
- package/package.json +54 -0
- package/scripts/bundle-vendor.js +110 -0
- package/scripts/lamaste-cert-info +62 -0
- package/scripts/lamaste-certbot +96 -0
- package/scripts/lamaste-priv +693 -0
- package/src/index.js +646 -0
- package/src/lib/accounts.js +86 -0
- package/src/lib/binaries.js +137 -0
- package/src/lib/cert-help-page.js +160 -0
- package/src/lib/env.js +132 -0
- package/src/lib/http-redirect.js +38 -0
- package/src/lib/ownership.js +231 -0
- package/src/lib/relay-services.js +103 -0
- package/src/lib/secrets.js +19 -0
- package/src/lib/service-config.js +311 -0
- package/src/lib/summary.js +131 -0
- package/src/lib/wrappers.js +63 -0
- package/src/tasks/gatekeeper.js +233 -0
- package/src/tasks/harden.js +293 -0
- package/src/tasks/mtls.js +193 -0
- package/src/tasks/nginx.js +265 -0
- package/src/tasks/node.js +110 -0
- package/src/tasks/panel.js +408 -0
- package/src/tasks/redeploy.js +438 -0
- package/vendor/docs/00-introduction/how-it-works.md +447 -0
- package/vendor/docs/00-introduction/quickstart.md +472 -0
- package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
- package/vendor/docs/01-concepts/authentication.md +464 -0
- package/vendor/docs/01-concepts/authorization.md +242 -0
- package/vendor/docs/01-concepts/certificates.md +452 -0
- package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
- package/vendor/docs/01-concepts/mtls.md +490 -0
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
- package/vendor/docs/01-concepts/security-model.md +600 -0
- package/vendor/docs/01-concepts/tickets.md +414 -0
- package/vendor/docs/01-concepts/tunneling.md +367 -0
- package/vendor/docs/02-guides/agent-setup.md +194 -0
- package/vendor/docs/02-guides/certificate-management.md +341 -0
- package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
- package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
- package/vendor/docs/02-guides/disaster-recovery.md +464 -0
- package/vendor/docs/02-guides/first-tunnel.md +230 -0
- package/vendor/docs/02-guides/installation.md +393 -0
- package/vendor/docs/02-guides/managing-users.md +255 -0
- package/vendor/docs/02-guides/onboarding.md +244 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
- package/vendor/docs/02-guides/static-sites.md +364 -0
- package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
- package/vendor/docs/03-architecture/installer-flow.md +151 -0
- package/vendor/docs/03-architecture/installer.md +445 -0
- package/vendor/docs/03-architecture/management-flow.md +288 -0
- package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
- package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
- package/vendor/docs/03-architecture/overview.md +541 -0
- package/vendor/docs/03-architecture/panel-client.md +416 -0
- package/vendor/docs/03-architecture/panel-server.md +695 -0
- package/vendor/docs/03-architecture/state-management.md +529 -0
- package/vendor/docs/03-architecture/system-overview.md +167 -0
- package/vendor/docs/04-api-reference/certificates.md +1141 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
- package/vendor/docs/04-api-reference/onboarding.md +473 -0
- package/vendor/docs/04-api-reference/overview.md +404 -0
- package/vendor/docs/04-api-reference/services.md +267 -0
- package/vendor/docs/04-api-reference/settings.md +343 -0
- package/vendor/docs/04-api-reference/sites.md +716 -0
- package/vendor/docs/04-api-reference/system.md +268 -0
- package/vendor/docs/04-api-reference/tickets.md +618 -0
- package/vendor/docs/04-api-reference/tunnels.md +417 -0
- package/vendor/docs/04-api-reference/users.md +333 -0
- package/vendor/docs/05-operations/backup-and-restore.md +342 -0
- package/vendor/docs/05-operations/monitoring.md +285 -0
- package/vendor/docs/05-operations/uninstalling.md +344 -0
- package/vendor/docs/05-operations/upgrades.md +270 -0
- package/vendor/docs/06-reference/config-files.md +1080 -0
- package/vendor/docs/06-reference/glossary.md +528 -0
- package/vendor/docs/06-reference/installer-flags.md +260 -0
- package/vendor/docs/06-reference/ports-and-services.md +244 -0
- package/vendor/docs/06-reference/troubleshooting.md +665 -0
- package/vendor/docs/_index.json +233 -0
- package/vendor/docs/gatekeeper-design.md +953 -0
- package/vendor/docs/package.json +15 -0
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
- package/vendor/gatekeeper/dist/boot.d.ts +1 -0
- package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/boot.js +17 -0
- package/vendor/gatekeeper/dist/boot.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.js +45 -0
- package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
- package/vendor/gatekeeper/dist/index.d.ts +9 -0
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/index.js +13 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.js +42 -0
- package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.js +79 -0
- package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.js +314 -0
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.js +247 -0
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.js +79 -0
- package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
- package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.js +151 -0
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.js +2 -0
- package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
- package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/index.js +251 -0
- package/vendor/gatekeeper/dist/server/index.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
- package/vendor/gatekeeper/package.json +49 -0
- package/vendor/server/bin/lamaste-server.js +204 -0
- package/vendor/server/package.json +31 -0
- package/vendor/server/src/commands/certs.js +224 -0
- package/vendor/server/src/commands/chisel.js +132 -0
- package/vendor/server/src/commands/logs.js +49 -0
- package/vendor/server/src/commands/plugins.js +321 -0
- package/vendor/server/src/commands/reset-admin.js +478 -0
- package/vendor/server/src/commands/restart.js +94 -0
- package/vendor/server/src/commands/sites.js +257 -0
- package/vendor/server/src/commands/status.js +158 -0
- package/vendor/server/src/commands/tunnels.js +365 -0
- package/vendor/server/src/commands/uninstall.js +326 -0
- package/vendor/server/src/config.js +38 -0
- package/vendor/server/src/exec.js +26 -0
- package/vendor/server/src/logger.js +39 -0
- package/vendor/server/src/ndjson.js +56 -0
- package/vendor/server/src/panel-api.js +85 -0
- package/vendor/server/src/state.js +45 -0
- package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
- package/vendor/server-ui/dist/index.html +13 -0
- package/vendor/serverd/package.json +44 -0
- package/vendor/serverd/src/boot.js +13 -0
- package/vendor/serverd/src/index.js +191 -0
- package/vendor/serverd/src/lib/app-error.js +14 -0
- package/vendor/serverd/src/lib/authelia.js +85 -0
- package/vendor/serverd/src/lib/certbot.js +74 -0
- package/vendor/serverd/src/lib/chisel-args.js +6 -0
- package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
- package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
- package/vendor/serverd/src/lib/chisel-users.js +109 -0
- package/vendor/serverd/src/lib/chisel.js +81 -0
- package/vendor/serverd/src/lib/config.js +190 -0
- package/vendor/serverd/src/lib/constants.js +8 -0
- package/vendor/serverd/src/lib/csr-signing.js +331 -0
- package/vendor/serverd/src/lib/enrollment.js +302 -0
- package/vendor/serverd/src/lib/files.js +19 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
- package/vendor/serverd/src/lib/invite-page.js +281 -0
- package/vendor/serverd/src/lib/ip-utils.js +79 -0
- package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
- package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
- package/vendor/serverd/src/lib/mtls.js +795 -0
- package/vendor/serverd/src/lib/nginx.js +1090 -0
- package/vendor/serverd/src/lib/pki-paths.js +41 -0
- package/vendor/serverd/src/lib/pki-sign.js +103 -0
- package/vendor/serverd/src/lib/plugins.js +538 -0
- package/vendor/serverd/src/lib/push-install.js +390 -0
- package/vendor/serverd/src/lib/revocation.js +112 -0
- package/vendor/serverd/src/lib/services.js +131 -0
- package/vendor/serverd/src/lib/session.js +151 -0
- package/vendor/serverd/src/lib/state-db.js +130 -0
- package/vendor/serverd/src/lib/state.js +140 -0
- package/vendor/serverd/src/lib/storage.js +63 -0
- package/vendor/serverd/src/lib/system-stats.js +58 -0
- package/vendor/serverd/src/lib/tickets.js +1556 -0
- package/vendor/serverd/src/lib/totp.js +76 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
- package/vendor/serverd/src/lib/user-access-session.js +142 -0
- package/vendor/serverd/src/lib/user-access.js +52 -0
- package/vendor/serverd/src/middleware/errors.js +63 -0
- package/vendor/serverd/src/middleware/mtls.js +120 -0
- package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
- package/vendor/serverd/src/middleware/role-guard.js +58 -0
- package/vendor/serverd/src/middleware/twofa-session.js +65 -0
- package/vendor/serverd/src/middleware/user-access-session.js +52 -0
- package/vendor/serverd/src/routes/enrollment.js +120 -0
- package/vendor/serverd/src/routes/health.js +22 -0
- package/vendor/serverd/src/routes/invite.js +163 -0
- package/vendor/serverd/src/routes/management/agents.js +182 -0
- package/vendor/serverd/src/routes/management/certs.js +911 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
- package/vendor/serverd/src/routes/management/identity.js +107 -0
- package/vendor/serverd/src/routes/management/invitations.js +166 -0
- package/vendor/serverd/src/routes/management/logs.js +218 -0
- package/vendor/serverd/src/routes/management/plugins.js +560 -0
- package/vendor/serverd/src/routes/management/services.js +63 -0
- package/vendor/serverd/src/routes/management/settings.js +325 -0
- package/vendor/serverd/src/routes/management/sites.js +522 -0
- package/vendor/serverd/src/routes/management/storage.js +192 -0
- package/vendor/serverd/src/routes/management/system.js +201 -0
- package/vendor/serverd/src/routes/management/tickets.js +457 -0
- package/vendor/serverd/src/routes/management/tunnels.js +709 -0
- package/vendor/serverd/src/routes/management/users.js +377 -0
- package/vendor/serverd/src/routes/management.js +36 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
- package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
- package/vendor/serverd/src/routes/onboarding/index.js +18 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
- package/vendor/serverd/src/routes/onboarding/status.js +12 -0
- package/vendor/serverd/src/routes/plugin-router.js +79 -0
- package/vendor/serverd/src/routes/user-access.js +389 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Tunnels API
|
|
2
|
+
|
|
3
|
+
> Create, list, move, and delete tunnels that expose services on an agent machine through your Lamaste domain.
|
|
4
|
+
|
|
5
|
+
## In Plain English
|
|
6
|
+
|
|
7
|
+
A tunnel connects a web app running on an agent machine (say, a development server on port 3000) to a public subdomain on your Lamaste domain (like `app.example.com`). When someone visits that URL, the request travels through the tunnel back to that machine.
|
|
8
|
+
|
|
9
|
+
Every tunnel belongs to exactly one **agent** — the enrolled machine that carries it. Only that agent is told about the tunnel, and only that agent's credential may open it on the server. Another agent cannot take over the hostname by binding the same port.
|
|
10
|
+
|
|
11
|
+
## Authentication
|
|
12
|
+
|
|
13
|
+
All tunnel endpoints require a valid mTLS client certificate and a completed onboarding. See the [API Overview](./overview.md) for details.
|
|
14
|
+
|
|
15
|
+
If onboarding is not complete, all endpoints return `503 Service Unavailable`.
|
|
16
|
+
|
|
17
|
+
**Admin vs agent certificates.** An admin certificate sees and manages every tunnel. An agent certificate sees and manages only the tunnels that agent carries: other agents' tunnels are absent from its list and answer `404` to it.
|
|
18
|
+
|
|
19
|
+
## Endpoints
|
|
20
|
+
|
|
21
|
+
### `GET /api/tunnels`
|
|
22
|
+
|
|
23
|
+
Returns tunnels, sorted by creation date (newest first). An agent certificate receives only its own tunnels.
|
|
24
|
+
|
|
25
|
+
**Query parameters:** `limit` (1–500, default 100), `offset` (default 0), `sort` (`createdAt` | `name`), `order` (`asc` | `desc`, default `desc`).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -s --cert client.p12:password \
|
|
29
|
+
https://203.0.113.42:9292/api/tunnels | jq
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Response (200):**
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"tunnels": [
|
|
37
|
+
{
|
|
38
|
+
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
39
|
+
"subdomain": "app",
|
|
40
|
+
"fqdn": "app.example.com",
|
|
41
|
+
"port": 3000,
|
|
42
|
+
"description": "React development server",
|
|
43
|
+
"type": "app",
|
|
44
|
+
"accessMode": "restricted",
|
|
45
|
+
"agentLabel": "laptop",
|
|
46
|
+
"enabled": true,
|
|
47
|
+
"createdAt": "2026-03-13T14:30:00.000Z"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"total": 1,
|
|
51
|
+
"limit": 100,
|
|
52
|
+
"offset": 0
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Field | Type | Description |
|
|
57
|
+
| --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `id` | `string` | UUID v4 identifier |
|
|
59
|
+
| `subdomain` | `string` | The subdomain portion (e.g., `app`) |
|
|
60
|
+
| `fqdn` | `string` | Fully qualified domain name (e.g., `app.example.com`) |
|
|
61
|
+
| `port` | `number` | Port the carrying agent forwards; bound on `127.0.0.1` on both the server and the agent |
|
|
62
|
+
| `description` | `string \| null` | Optional human-readable description |
|
|
63
|
+
| `type` | `string` | `'app'` (default), `'panel'` (agent web panel), or `'plugin'` |
|
|
64
|
+
| `accessMode` | `string` | `'restricted'` (default), `'authenticated'`, or `'public'` — absent on panel tunnels |
|
|
65
|
+
| `agentLabel` | `string` | The agent that carries the tunnel. Absent only on an unassigned legacy tunnel (see below) |
|
|
66
|
+
| `maxBodySizeMb` | `number` | Largest request body nginx accepts, in MiB. Absent on panel tunnels and on tunnels created before the setting existed, which keep nginx's built-in 1 MiB until a body limit is set on them |
|
|
67
|
+
| `enabled` | `boolean` | Whether the tunnel is active (defaults to `true`) |
|
|
68
|
+
| `createdAt` | `string` | ISO 8601 timestamp |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### `POST /api/tunnels`
|
|
73
|
+
|
|
74
|
+
Creates a new tunnel. This is a multi-step operation that:
|
|
75
|
+
|
|
76
|
+
1. Issues a Let's Encrypt TLS certificate for `<subdomain>.<domain>`
|
|
77
|
+
2. Writes an nginx vhost configuration for the subdomain
|
|
78
|
+
3. Saves the tunnel to the state file
|
|
79
|
+
4. Syncs Chisel: re-renders the authfile so the carrying agent may bind the new port (Chisel reloads it without a restart)
|
|
80
|
+
|
|
81
|
+
State is saved before Chisel is synced because the Chisel authfile is rendered from the persisted state. If any step fails, the previous steps are rolled back. The whole workflow — validation included — runs under the panel's single tunnel lock, so two concurrent requests cannot both claim the same subdomain or port.
|
|
82
|
+
|
|
83
|
+
The carrying agent's sync timer picks the tunnel up within about 30 seconds and starts forwarding the port; `lamaste-agent update` on the agent applies it immediately.
|
|
84
|
+
|
|
85
|
+
**Request:**
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"subdomain": "app",
|
|
90
|
+
"port": 3000,
|
|
91
|
+
"agentLabel": "laptop",
|
|
92
|
+
"description": "React development server",
|
|
93
|
+
"accessMode": "restricted",
|
|
94
|
+
"maxBodySizeMb": 10
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Field | Type | Validation | Description |
|
|
99
|
+
| --------------- | --------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `subdomain` | `string` | Lowercase alphanumeric + hyphens, max 63 chars, cannot start/end with hyphen | The subdomain to create |
|
|
101
|
+
| `port` | `integer` | 1024 - 65535, not a [reserved port](#port) | Port the agent forwards |
|
|
102
|
+
| `agentLabel` | `string` | An enrolled, non-revoked agent's label | **Required for admin certificates.** An agent certificate may omit it (defaults to itself) but may not name another agent |
|
|
103
|
+
| `description` | `string` | Max 200 chars, optional (defaults to `""`) | Human-readable description |
|
|
104
|
+
| `type` | `string` | `'app'` (default), `'panel'`, or `'plugin'` | `panel` requires the `panel:expose` capability; `plugin` is admin-only |
|
|
105
|
+
| `accessMode` | `string` | `'restricted'` (default), `'authenticated'`, `'public'` | `authenticated` and `public` are admin-only |
|
|
106
|
+
| `maxBodySizeMb` | `integer` | 1 - 10240 for admins, 1 - 100 for agents; optional (defaults to 10) | Written into the vhost as `client_max_body_size`; larger requests get HTTP 413. Raise it for apps that take big uploads |
|
|
107
|
+
|
|
108
|
+
**Subdomain regex:**
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Reserved subdomains** (cannot be used):
|
|
115
|
+
|
|
116
|
+
`panel`, `auth`, `tunnel`, `www`, `mail`, `ftp`, `api`
|
|
117
|
+
|
|
118
|
+
**Reserved prefix:** Subdomains starting with `agent-` are reserved for agent panel tunnels (created via `POST /api/tunnels/expose-panel`). Regular tunnel creation with `agent-` prefixed subdomains is rejected unless `type` is `'panel'`.
|
|
119
|
+
|
|
120
|
+
**Who may create:** an admin certificate, or an agent certificate with `tunnels:write` for a tunnel it carries itself. A **plugin-agent** certificate (`CN=plugin-agent:...`) cannot create tunnels: it belongs to a plugin running on an agent, not to a machine running a Chisel client, so it holds no Chisel credential and could carry nothing. Create a plugin's tunnel with the agent's own certificate or as an administrator.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
curl -s --cert client.p12:password \
|
|
124
|
+
-X POST \
|
|
125
|
+
-H "Content-Type: application/json" \
|
|
126
|
+
-d '{"subdomain":"app","port":3000,"agentLabel":"laptop","description":"React dev server"}' \
|
|
127
|
+
https://203.0.113.42:9292/api/tunnels | jq
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Response (201):**
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"ok": true,
|
|
135
|
+
"tunnel": {
|
|
136
|
+
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
137
|
+
"subdomain": "app",
|
|
138
|
+
"fqdn": "app.example.com",
|
|
139
|
+
"port": 3000,
|
|
140
|
+
"description": "React dev server",
|
|
141
|
+
"type": "app",
|
|
142
|
+
"accessMode": "restricted",
|
|
143
|
+
"agentLabel": "laptop",
|
|
144
|
+
"maxBodySizeMb": 10,
|
|
145
|
+
"enabled": true,
|
|
146
|
+
"createdAt": "2026-03-13T14:30:00.000Z"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Errors:**
|
|
152
|
+
|
|
153
|
+
| Status | Body | When |
|
|
154
|
+
| ------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
155
|
+
| 400 | `{"error":"Validation failed","details":{"issues":[...]}}` | Invalid subdomain format, port out of range or reserved |
|
|
156
|
+
| 400 | `{"error":"agentLabel is required: name the agent that will carry the tunnel"}` | Admin certificate did not name the carrying agent |
|
|
157
|
+
| 400 | `{"error":"Failed to create tunnel","details":"Agent 'x' is not enrolled or has been revoked"}` | `agentLabel` does not name an active agent |
|
|
158
|
+
| 400 | `{"error":"Failed to create tunnel","details":"Subdomain 'panel' is reserved"}` | Subdomain is in the reserved list |
|
|
159
|
+
| 400 | `{"error":"Failed to create tunnel","details":"Subdomain 'app' is already in use"}` | Another tunnel uses this subdomain |
|
|
160
|
+
| 400 | `{"error":"Failed to create tunnel","details":"Port 3000 is already in use by another tunnel"}` | Another tunnel uses this port |
|
|
161
|
+
| 400 | `{"error":"Failed to create tunnel","details":"Domain 'app.example.com' is already served by a static site"}` | A static site or site alias already answers on this hostname |
|
|
162
|
+
| 400 | `{"error":"Domain and email must be configured before creating tunnels"}` | Domain not set in config |
|
|
163
|
+
| 403 | `{"error":"Agents can only create tunnels they carry themselves"}` | Agent certificate named another agent |
|
|
164
|
+
| 403 | `{"error":"Plugin-agent certificates cannot create tunnels — use the agent certificate of ..."}` | Plugin-agent certificate |
|
|
165
|
+
| 403 | `{"error":"Only administrators can set a body limit above 100 MiB"}` | Agent certificate asked for `maxBodySizeMb` over 100 |
|
|
166
|
+
| 403 | `{"error":"Only administrators can set tunnel access mode to public or authenticated"}` | Agent certificate asked for a non-restricted mode |
|
|
167
|
+
| 500 | `{"error":"Failed to create tunnel","details":"Certificate issuance failed: ..."}` | certbot failed |
|
|
168
|
+
| 500 | `{"error":"Failed to create tunnel","details":"Nginx configuration failed: ..."}` | nginx vhost write or test failed |
|
|
169
|
+
| 500 | `{"error":"Failed to create tunnel","details":"State persistence failed: ..."}` | Failed to write tunnels.json |
|
|
170
|
+
| 500 | `{"error":"Failed to create tunnel","details":"Chisel reconfiguration failed: ..."}` | Chisel authfile sync failed |
|
|
171
|
+
|
|
172
|
+
### Creation Flow
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
Take the tunnel lock (held until the response)
|
|
176
|
+
│
|
|
177
|
+
Validate input
|
|
178
|
+
├── Check agentLabel names an active agent
|
|
179
|
+
├── Check subdomain not reserved
|
|
180
|
+
├── Check subdomain uniqueness
|
|
181
|
+
├── Check port not reserved
|
|
182
|
+
└── Check port uniqueness
|
|
183
|
+
│
|
|
184
|
+
▼
|
|
185
|
+
Step 1: certbot issues TLS cert for <subdomain>.<domain>
|
|
186
|
+
│
|
|
187
|
+
▼
|
|
188
|
+
Step 2: Write nginx vhost for the subdomain
|
|
189
|
+
│ (rollback: remove vhost on failure)
|
|
190
|
+
▼
|
|
191
|
+
Step 3: Save tunnel to tunnels.json
|
|
192
|
+
│ (rollback: remove vhost on failure)
|
|
193
|
+
▼
|
|
194
|
+
Step 4: Sync Chisel — authfile grants the owner the port (hot-reloaded)
|
|
195
|
+
│ (rollback: remove tunnel from state, remove vhost)
|
|
196
|
+
▼
|
|
197
|
+
Return 201 with tunnel object
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### `PATCH /api/tunnels/:id`
|
|
203
|
+
|
|
204
|
+
Changes any of: carrying agent, access mode, request body limit, enabled state. All of them are applied in one workflow under the tunnel lock, in that order — ownership first, so a later failure still leaves the tunnel with the agent the caller asked for; access mode and body limit next; the enabled state last, so a tunnel being disabled is never re-enabled by the vhost rewrite before it. Authorization is checked against the tunnel as it is when the lock is taken.
|
|
205
|
+
|
|
206
|
+
- **`enabled`** — when disabled, the tunnel's nginx vhost symlink is removed (the config file is kept) and the owner's Chisel grant for the port is withdrawn (Chisel restarts, which ends sessions still holding it). Re-enabling restores both. Only enabled tunnels appear in an agent's config, and the owner's sync timer drops or adds the remote within about 30 seconds.
|
|
207
|
+
- **`agentLabel`** (admin only) — moves the tunnel to another active agent. The previous owner loses the port grant in the same Chisel sync that gives it to the new owner. Both agents' sync timers apply the move within about 30 seconds; `lamaste-agent update` applies it at once. Agent panel tunnels (`type: 'panel'`) belong to the agent they expose and cannot be moved.
|
|
208
|
+
- **`accessMode`** and **`maxBodySizeMb`** — both live in the tunnel's nginx vhost, which is re-rendered and tested (nginx keeps the previous vhost if the test fails). Gatekeeper watches the tunnel state and picks up an access-mode change on its own. A disabled tunnel stays disabled. `public` and `authenticated` are admin-only, as on creation. Agent panel tunnels have a fixed mTLS vhost and cannot be reconfigured.
|
|
209
|
+
|
|
210
|
+
**Request:**
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"enabled": false,
|
|
215
|
+
"agentLabel": "build-server"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| Field | Type | Validation | Description |
|
|
220
|
+
| --------------- | --------- | -------------------------------------- | --------------------------------------- |
|
|
221
|
+
| `enabled` | `boolean` | Optional | Whether the tunnel should be active |
|
|
222
|
+
| `agentLabel` | `string` | Optional; an active agent; admin only | The agent that should carry the tunnel |
|
|
223
|
+
| `accessMode` | `string` | Optional; non-restricted is admin only | `restricted`, `authenticated`, `public` |
|
|
224
|
+
| `maxBodySizeMb` | `integer` | Optional; 1 - 10240 (agents: 1 - 100) | Largest request body in MiB |
|
|
225
|
+
|
|
226
|
+
At least one field is required; unknown fields are rejected.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
curl -s --cert client.p12:password \
|
|
230
|
+
-X PATCH \
|
|
231
|
+
-H "Content-Type: application/json" \
|
|
232
|
+
-d '{"agentLabel":"build-server"}' \
|
|
233
|
+
https://203.0.113.42:9292/api/tunnels/a1b2c3d4-e5f6-7890-abcd-ef1234567890 | jq
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Response (200):** `{ "ok": true, "tunnel": { ... } }` with the updated tunnel object.
|
|
237
|
+
|
|
238
|
+
**Errors:**
|
|
239
|
+
|
|
240
|
+
| Status | Body | When |
|
|
241
|
+
| ------ | ----------------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
242
|
+
| 400 | `{"error":"Validation failed",...}` | Empty body, unknown field, or bad label format |
|
|
243
|
+
| 400 | `{"error":"Failed to update tunnel","details":"Agent 'x' is not enrolled or has been revoked"}` | `agentLabel` does not name an active agent |
|
|
244
|
+
| 400 | `{"error":"Failed to update tunnel","details":"Agent panel tunnels belong to ..."}` | Tried to move a panel tunnel |
|
|
245
|
+
| 403 | `{"error":"Only administrators can change which agent carries a tunnel"}` | Agent certificate sent `agentLabel` |
|
|
246
|
+
| 403 | `{"error":"Only administrators can set a body limit above 100 MiB"}` | Agent certificate sent `maxBodySizeMb` > 100 |
|
|
247
|
+
| 404 | `{"error":"Tunnel not found"}` | No such tunnel, or it belongs to another agent |
|
|
248
|
+
| 500 | `{"error":"Failed to update tunnel","details":"..."}` | nginx, Chisel, or state operation failed |
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### `DELETE /api/tunnels/:id`
|
|
253
|
+
|
|
254
|
+
Deletes a tunnel by its UUID: removes the nginx vhost, removes the tunnel from the state file, and syncs Chisel so the owner's grant for the port is withdrawn.
|
|
255
|
+
|
|
256
|
+
The TLS certificate is not deleted (it is harmless to keep and may be reused if the subdomain is recreated).
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
curl -s --cert client.p12:password \
|
|
260
|
+
-X DELETE \
|
|
261
|
+
https://203.0.113.42:9292/api/tunnels/a1b2c3d4-e5f6-7890-abcd-ef1234567890 | jq
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Response (200):** `{ "ok": true }`
|
|
265
|
+
|
|
266
|
+
**Errors:**
|
|
267
|
+
|
|
268
|
+
| Status | Body | When |
|
|
269
|
+
| ------ | ----------------------------------------------------- | ---------------------------------------------- |
|
|
270
|
+
| 404 | `{"error":"Tunnel not found"}` | No such tunnel, or it belongs to another agent |
|
|
271
|
+
| 500 | `{"error":"Failed to delete tunnel","details":"..."}` | nginx, Chisel, or state operation failed |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### `GET /api/tunnels/agent-config`
|
|
276
|
+
|
|
277
|
+
Returns the Chisel client configuration for one agent: the enabled tunnels it carries that the Chisel authfile actually grants it, and nothing else. A tunnel on a reserved port or sharing its port with another tunnel (possible only in state written before those checks) is withheld — Chisel would refuse the agent's whole session for it. Used by `lamaste-agent setup`, `update`, and the agent's 30-second sync timer (`lamaste-agent sync`), on both macOS and Linux.
|
|
278
|
+
|
|
279
|
+
**Required capability:** `tunnels:read`
|
|
280
|
+
|
|
281
|
+
**Query parameters:**
|
|
282
|
+
|
|
283
|
+
| Parameter | Description |
|
|
284
|
+
| --------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
285
|
+
| `agent` | Admin certificates only: the agent whose config to return. Ignored for agent certificates, which always get their own. |
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
curl -s --cert client.p12:password \
|
|
289
|
+
https://203.0.113.42:9292/api/tunnels/agent-config | jq
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Response (200) — agent certificate, or admin with `?agent=`:**
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"domain": "example.com",
|
|
297
|
+
"chiselServerUrl": "https://tunnel.example.com:443",
|
|
298
|
+
"agentLabel": "laptop",
|
|
299
|
+
"chiselArgs": ["client", "https://tunnel.example.com:443", "R:127.0.0.1:3000:127.0.0.1:3000"],
|
|
300
|
+
"chiselCredentialIssuedAt": "2026-09-20T10:12:00.000Z",
|
|
301
|
+
"tunnels": [{ "port": 3000, "subdomain": "app" }]
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Response (200) — admin without `?agent=`:** `{ "domain": "...", "chiselServerUrl": "..." }` only. A Chisel configuration is meaningless without an agent to carry it.
|
|
306
|
+
|
|
307
|
+
| Field | Type | Description |
|
|
308
|
+
| -------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
309
|
+
| `domain` | `string` | Base domain |
|
|
310
|
+
| `chiselServerUrl` | `string` | Full URL to the Chisel server endpoint |
|
|
311
|
+
| `agentLabel` | `string` | The agent this configuration is for |
|
|
312
|
+
| `chiselArgs` | `string[]` | Chisel client arguments: `client`, the server URL, and one `R:127.0.0.1:<port>:127.0.0.1:<port>` remote per tunnel. No `--tls-skip-verify` (the agent verifies the relay's certificate) and no credential — the agent supplies its own through the service environment, never argv |
|
|
313
|
+
| `chiselCredentialIssuedAt` | `string \| null` | When the agent's current Chisel credential was issued — a timestamp, never the credential. An agent holding a credential with a different issue time (an administrator rotated it) fetches the current one from `GET /api/agents/me/chisel-credential` |
|
|
314
|
+
| `tunnels` | `array` | The agent's enabled tunnels with port and subdomain |
|
|
315
|
+
|
|
316
|
+
**Errors:**
|
|
317
|
+
|
|
318
|
+
| Status | Body | When |
|
|
319
|
+
| ------ | --------------------------------------------- | ------------------------ |
|
|
320
|
+
| 400 | `{"error":"Domain not configured"}` | Domain has not been set |
|
|
321
|
+
| 500 | `{"error":"Failed to generate agent config"}` | Config generation failed |
|
|
322
|
+
|
|
323
|
+
## Chisel Grants
|
|
324
|
+
|
|
325
|
+
The Chisel server authenticates every client against `/etc/lamalibre/lamaste/chisel-users`. That file is rendered by the panel from two persisted inputs — the per-agent credential store and the tunnel state — and re-rendered whenever either changes:
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
{
|
|
329
|
+
"lamaste-no-grants:<random>": [],
|
|
330
|
+
"agent-laptop:<password>": ["^R:127\\.0\\.0\\.1:3000$"],
|
|
331
|
+
"agent-build-server:<password>": []
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
- Each `agent-<label>` user may reverse-bind exactly the ports of the enabled tunnels it owns. An agent with no tunnels can connect but bind nothing. The patterns are anchored because Chisel matches them unanchored — `^…$` is what keeps port `2001` from also granting `20010`.
|
|
336
|
+
- `lamaste-no-grants` exists so the file is never empty. Chisel disables authentication entirely when its authfile has no users, which would turn a freshly installed server (or one whose agents are all revoked) into an open relay. Its password is random, stored at `chisel-sentinel` (0600), and never handed out.
|
|
337
|
+
- A port that belongs to a Lamaste service ([reserved ports](#port)), or a port that two tunnels claim, is never granted — even if the state file somehow holds such a tunnel.
|
|
338
|
+
- The file is `0640`, owner `lamaste`, group `lamaste-chisel` — the group the Chisel server runs as (`User=nobody`, `Group=lamaste-chisel`). The panel is a member of that group and writes the file itself, without sudo: a `0600` temp file in the same directory, `chgrp`, `chmod 0640`, fsync, rename. Chisel therefore never sees the file with the wrong permissions.
|
|
339
|
+
- Chisel 1.12.0 reloads the authfile on every replace, and the reload applies to new sessions. Additions (a new agent, a new grant) therefore take effect without a restart. A revocation (a removed or rotated password, a withdrawn grant) restarts Chisel with `systemctl try-restart`, because a live session keeps what it already bound — and `try-restart` leaves a deliberately stopped Chisel stopped.
|
|
340
|
+
|
|
341
|
+
### Why the grants matter to the agent
|
|
342
|
+
|
|
343
|
+
Chisel rejects a client's **whole** session when one remote it requests is not granted. An agent still asking for a disabled or moved tunnel's port would lose all of its other tunnels too. That is why agents run a sync timer that drops such remotes within about 30 seconds — see [Agent Setup](../02-guides/agent-setup.md#how-tunnel-changes-reach-the-agent).
|
|
344
|
+
|
|
345
|
+
### Upgrading from a version without tunnel ownership
|
|
346
|
+
|
|
347
|
+
On startup the panel reconciles older state. An agent panel tunnel without an `agentLabel` is bound to the agent its hostname names (`agent-<label>`) when that agent is active, and never to any other. Other tunnels without an `agentLabel` are bound to the only active machine agent when exactly one exists (plugin-agent certificates carry no tunnels and are not counted). With several active agents the carrier cannot be known, so those tunnels stay **unassigned**: they are carried by no agent, show as `unassigned` in the panel, are logged at startup, and come back as soon as an administrator moves them to an agent (`PATCH` with `agentLabel`).
|
|
348
|
+
|
|
349
|
+
Tunnels on a reserved port, or two tunnels on the same port, are granted to no agent and left out of every `agent-config`; the panel logs them at startup. Delete each and recreate it on a free port.
|
|
350
|
+
|
|
351
|
+
### When an agent certificate is revoked
|
|
352
|
+
|
|
353
|
+
Revoking an agent's certificate releases its tunnels: its app and plugin tunnels become unassigned (their hostnames stay reserved and dark until an administrator assigns them), and its `agent-<label>` panel tunnel is removed with its vhost. A later enrollment that reuses the label therefore inherits nothing.
|
|
354
|
+
|
|
355
|
+
## Validation Rules
|
|
356
|
+
|
|
357
|
+
### Subdomain
|
|
358
|
+
|
|
359
|
+
- Lowercase letters, digits, and hyphens only
|
|
360
|
+
- Cannot start or end with a hyphen
|
|
361
|
+
- Maximum 63 characters
|
|
362
|
+
- Must not be one of: `panel`, `auth`, `tunnel`, `www`, `mail`, `ftp`, `api`
|
|
363
|
+
- Must not start with `agent-` (reserved for agent panel tunnels, unless `type` is `'panel'`)
|
|
364
|
+
- Must be unique across all tunnels
|
|
365
|
+
- Must not be a hostname a static site or site alias already serves (checked on tunnel creation; site creation and alias changes check the reverse)
|
|
366
|
+
|
|
367
|
+
### Port
|
|
368
|
+
|
|
369
|
+
- Must be an integer
|
|
370
|
+
- Minimum: 1024 (no privileged ports)
|
|
371
|
+
- Maximum: 65535
|
|
372
|
+
- Must not be a reserved port: `3100` (panel server), `9090` (Chisel server), `9091` (Authelia), `9292` (IP-based panel listener), `9294` (Gatekeeper)
|
|
373
|
+
- Must be unique across all tunnels — Chisel binds the same port on the server and on the agent
|
|
374
|
+
|
|
375
|
+
A tunnel's vhost proxies to `127.0.0.1:<port>` on the relay, so a tunnel on one of Lamaste's own ports would publish that internal service on the internet. For the panel ports that would be an administrator takeover, not a leak: the panel trusts the client-certificate headers nginx sets. The reserved ports are rejected by the request schema, again inside the tunnel workflow, and never granted in the Chisel authfile.
|
|
376
|
+
|
|
377
|
+
### Agent label
|
|
378
|
+
|
|
379
|
+
- An enrolled, non-revoked agent: `^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$`
|
|
380
|
+
|
|
381
|
+
## Quick Reference
|
|
382
|
+
|
|
383
|
+
| Method | Path | Description |
|
|
384
|
+
| ------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
385
|
+
| GET | `/api/tunnels` | List tunnels (an agent sees only its own) |
|
|
386
|
+
| POST | `/api/tunnels` | Create a tunnel carried by an agent |
|
|
387
|
+
| PATCH | `/api/tunnels/:id` | Move (admin), access mode, body limit, enable/disable |
|
|
388
|
+
| DELETE | `/api/tunnels/:id` | Delete a tunnel by UUID |
|
|
389
|
+
| GET | `/api/tunnels/agent-config` | One agent's Chisel client config (macOS & Linux) |
|
|
390
|
+
| GET | `/api/tunnels/agent-panel-status` | Check if agent has an exposed panel tunnel |
|
|
391
|
+
| POST | `/api/tunnels/expose-panel` | Create mTLS panel tunnel for requesting agent (refused when a static site or alias serves `agent-<label>.<domain>`) |
|
|
392
|
+
| DELETE | `/api/tunnels/retract-panel` | Remove the panel tunnel for requesting agent |
|
|
393
|
+
|
|
394
|
+
### curl Cheat Sheet
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# List tunnels
|
|
398
|
+
curl -s --cert client.p12:password \
|
|
399
|
+
https://203.0.113.42:9292/api/tunnels | jq
|
|
400
|
+
|
|
401
|
+
# Create a tunnel carried by the agent "laptop"
|
|
402
|
+
curl -s --cert client.p12:password \
|
|
403
|
+
-X POST -H "Content-Type: application/json" \
|
|
404
|
+
-d '{"subdomain":"myapp","port":8080,"agentLabel":"laptop"}' \
|
|
405
|
+
https://203.0.113.42:9292/api/tunnels | jq
|
|
406
|
+
|
|
407
|
+
# Move it to another agent
|
|
408
|
+
curl -s --cert client.p12:password \
|
|
409
|
+
-X PATCH -H "Content-Type: application/json" \
|
|
410
|
+
-d '{"agentLabel":"build-server"}' \
|
|
411
|
+
https://203.0.113.42:9292/api/tunnels/<uuid> | jq
|
|
412
|
+
|
|
413
|
+
# Delete tunnel
|
|
414
|
+
curl -s --cert client.p12:password \
|
|
415
|
+
-X DELETE \
|
|
416
|
+
https://203.0.113.42:9292/api/tunnels/<uuid> | jq
|
|
417
|
+
```
|