@codions/portta 0.0.0-develop.149.sha-240655c → 0.0.0-develop.150.sha-ed65081
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/dist/cli.js +22823 -23160
- package/dist/documentation.json +1 -1
- package/dist/host.js +663 -271
- package/dist/runtime/.env.example +33 -48
- package/dist/runtime/VERSION +1 -1
- package/dist/runtime/docker/compose/features/panel-host.yaml +44 -0
- package/dist/runtime/docker/compose/features/taskflow.yaml +5 -28
- package/dist/runtime/docker/compose/features/web.yaml +10 -26
- package/package.json +2 -2
- package/dist/runtime/docker/compose/features/db.yaml +0 -63
- package/dist/runtime/docker/compose/features/panel-webhook.yaml +0 -46
|
@@ -182,9 +182,11 @@ PORTTA_WEB_READ_ONLY=false
|
|
|
182
182
|
|
|
183
183
|
# The panel's own front door, which is now the panel's own business.
|
|
184
184
|
#
|
|
185
|
-
# disabled every request is the local operator.
|
|
186
|
-
#
|
|
187
|
-
#
|
|
185
|
+
# disabled every request is the local operator. Legal wherever the set of
|
|
186
|
+
# people who can reach the panel is already an authenticated set:
|
|
187
|
+
# `local` (this machine), `tailscale` (a device somebody enrolled)
|
|
188
|
+
# and `vpn` (a client of the VPN). Refused at boot with `public`
|
|
189
|
+
# and `domain`, which answer whoever finds the address.
|
|
188
190
|
# required the panel signs people in. Accounts, roles, sessions, tokens and
|
|
189
191
|
# an optional second factor, all in its database. The first person
|
|
190
192
|
# is created at /setup, once, and creates everyone else.
|
|
@@ -192,6 +194,14 @@ PORTTA_WEB_READ_ONLY=false
|
|
|
192
194
|
# See docs/development/adr/0035-authentication-lives-in-the-panel.md and docs/product/guides/authentication.md.
|
|
193
195
|
PORTTA_AUTH_MODE=disabled
|
|
194
196
|
|
|
197
|
+
# The one case the access mode does not settle: `local` bound to something
|
|
198
|
+
# other than loopback is the LAN, and "everyone on this Wi-Fi" is not an
|
|
199
|
+
# authenticated set. Turning the login off there needs this said out loud.
|
|
200
|
+
#
|
|
201
|
+
# `portta doctor` warns while it is true, and refuses the combination while it
|
|
202
|
+
# is not.
|
|
203
|
+
PORTTA_AUTH_ALLOW_LAN=false
|
|
204
|
+
|
|
195
205
|
# Signs sessions, for the panel and for the ForwardAuth service in front of
|
|
196
206
|
# project hostnames. Generated by `portta bootstrap` and `portta web up` without
|
|
197
207
|
# ever being printed. Changing it signs everybody out.
|
|
@@ -224,18 +234,15 @@ PORTTA_RUNTIME_DOCS=true
|
|
|
224
234
|
# always available.
|
|
225
235
|
PORTTA_RUNTIME_API_DOCS=
|
|
226
236
|
|
|
227
|
-
# Panel persistence
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
PORTTA_RUNTIME_DB_USER=portta
|
|
237
|
-
PORTTA_RUNTIME_DB_PASSWORD=
|
|
238
|
-
PORTTA_RUNTIME_DATABASE_URL=
|
|
237
|
+
# Panel persistence: one SQLite file under this installation, at
|
|
238
|
+
# `state/panel/portta.db`. There is nothing to configure, no password to
|
|
239
|
+
# generate and no service to start — the file is created on first open
|
|
240
|
+
# (docs/development/adr/0037-sqlite-is-the-panel-database.md).
|
|
241
|
+
#
|
|
242
|
+
# This is the path INSIDE the panel container, and `state/panel` is bind-mounted
|
|
243
|
+
# onto it. Changing it is for a different mount, not for a different database;
|
|
244
|
+
# `portta db` operates the one this points at.
|
|
245
|
+
PORTTA_RUNTIME_DATABASE_FILE=/app/state/panel/portta.db
|
|
239
246
|
|
|
240
247
|
# The panel image. A normal installation pulls this; it never builds, because
|
|
241
248
|
# an installed PORTTA_HOME holds no application source. Pinned like every other
|
|
@@ -410,43 +417,21 @@ CF_DNS_API_TOKEN=
|
|
|
410
417
|
CLOUDFLARE_ZONE=
|
|
411
418
|
|
|
412
419
|
# ----------------------------------------------------------------------------
|
|
413
|
-
# GitHub
|
|
420
|
+
# Issues: GitHub through `gh`, and Linear
|
|
414
421
|
# ----------------------------------------------------------------------------
|
|
415
|
-
#
|
|
416
|
-
# is not a Traefik credential. With GITHUB_APP_ENABLED=false the panel makes no
|
|
417
|
-
# request to github.com and behaves exactly as it did without this section.
|
|
422
|
+
# Nothing is configured here, and that is the point.
|
|
418
423
|
#
|
|
419
|
-
# The
|
|
420
|
-
#
|
|
421
|
-
#
|
|
422
|
-
# docs/
|
|
423
|
-
|
|
424
|
-
GITHUB_APP_ENABLED=false
|
|
425
|
-
|
|
426
|
-
# The numeric App id from the App's settings page. Not a secret.
|
|
427
|
-
GITHUB_APP_ID=
|
|
428
|
-
|
|
429
|
-
# The .pem inside the panel container. `state/github/` is the only directory
|
|
430
|
-
# mounted there, so the path must begin /app/state/github/. The filename is
|
|
431
|
-
# yours: keep the one GitHub gave the download if you like.
|
|
432
|
-
GITHUB_APP_PRIVATE_KEY_FILE=/app/state/github/app.pem
|
|
433
|
-
|
|
434
|
-
# Verifies webhook deliveries. Generate one when you create the App.
|
|
435
|
-
GITHUB_APP_WEBHOOK_SECRET=
|
|
436
|
-
|
|
437
|
-
# GitHub Enterprise Server users: your API root, e.g. https://ghe.example.com/api/v3
|
|
438
|
-
GITHUB_API_URL=https://api.github.com
|
|
439
|
-
|
|
440
|
-
# How often the panel re-reads what changed on GitHub, in minutes.
|
|
424
|
+
# The panel reads and writes issues through the host daemon, which runs the
|
|
425
|
+
# GitHub CLI as whoever is signed in on the host — so the credential is `gh
|
|
426
|
+
# auth`'s, kept where `gh` keeps it, and never in this file or in the panel's
|
|
427
|
+
# container (docs/development/adr/0018-github-issues-through-the-gh-cli.md).
|
|
441
428
|
#
|
|
442
|
-
#
|
|
443
|
-
#
|
|
444
|
-
# asks for issues updated since the stored cursor, so a quiet repository costs
|
|
445
|
-
# one conditional request.
|
|
429
|
+
# gh auth login once, on the host
|
|
430
|
+
# portta host serve the daemon the panel reaches it through
|
|
446
431
|
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
|
|
432
|
+
# Linear is optional and works the same way: export LINEAR_API_KEY in the
|
|
433
|
+
# environment that runs `portta host serve`. It is deliberately not a variable
|
|
434
|
+
# here, because this file is one the panel itself can write.
|
|
450
435
|
|
|
451
436
|
# ----------------------------------------------------------------------------
|
|
452
437
|
# Cloudflare Tunnel: optional connector (credential stays in state/cloudflared)
|
package/dist/runtime/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.0-develop.
|
|
1
|
+
0.0.0-develop.150.sha-ed65081
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Portta: the panel's way to the host daemon
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# Applied whenever the panel is on.
|
|
5
|
+
#
|
|
6
|
+
# The panel is a container with no `gh`, no git, no tmux, no agent CLI and no
|
|
7
|
+
# project directory, and ADR 0001 keeps it that way. Work that needs the host
|
|
8
|
+
# runs in `portta host serve` instead, and the panel reaches it over HTTP and
|
|
9
|
+
# WebSocket through its authorised proxy
|
|
10
|
+
# (docs/development/adr/0047-host-daemon-and-panel-proxy.md).
|
|
11
|
+
#
|
|
12
|
+
# This used to ride with Task Flow, because Task Flow was the only thing that
|
|
13
|
+
# needed the host. Issues need it too, and Issues are the panel's work surface
|
|
14
|
+
# now (docs/development/adr/0018-github-issues-through-the-gh-cli.md) — so the
|
|
15
|
+
# connection is part of having a panel, and Task Flow is left as the switch for
|
|
16
|
+
# its own routes.
|
|
17
|
+
#
|
|
18
|
+
# What this adds to the panel, and nothing else
|
|
19
|
+
# ----------------------------------------------
|
|
20
|
+
# host.docker.internal a name for the host. Docker Desktop resolves it on
|
|
21
|
+
# its own; on Linux `host-gateway` maps it to the
|
|
22
|
+
# bridge gateway, which is where the daemon must bind
|
|
23
|
+
# (PORTTA_HOST_BIND=172.17.0.1, or the address of the
|
|
24
|
+
# bridge in use). The daemon's loopback default is not
|
|
25
|
+
# reachable from a container on Linux.
|
|
26
|
+
# state/host/token the daemon's token, read-only. `portta up` and
|
|
27
|
+
# `portta web up` create it first: a bind mount of a
|
|
28
|
+
# file that does not exist would become a directory.
|
|
29
|
+
#
|
|
30
|
+
# The panel never receives a project directory, a socket or the daemon's own
|
|
31
|
+
# databases: only a URL and a secret. A daemon that is not running is not a
|
|
32
|
+
# failure to start — it is a panel that reports which of its surfaces are
|
|
33
|
+
# unavailable and why.
|
|
34
|
+
# ============================================================================
|
|
35
|
+
|
|
36
|
+
services:
|
|
37
|
+
web:
|
|
38
|
+
extra_hosts:
|
|
39
|
+
- "host.docker.internal:host-gateway"
|
|
40
|
+
environment:
|
|
41
|
+
PORTTA_HOST_URL: ${PORTTA_HOST_URL:-http://host.docker.internal:${PORTTA_HOST_PORT:-5111}}
|
|
42
|
+
PORTTA_RUNTIME_HOST_TOKEN_FILE: /app/state/host/token
|
|
43
|
+
volumes:
|
|
44
|
+
- ./state/host/token:/app/state/host/token:ro
|
|
@@ -1,38 +1,15 @@
|
|
|
1
1
|
# ============================================================================
|
|
2
|
-
# Portta:
|
|
2
|
+
# Portta: Task Flow
|
|
3
3
|
# ============================================================================
|
|
4
4
|
# Applied when PORTTA_TASKFLOW=true and the panel is on.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# module proxy. See docs/development/adr/0047-host-daemon-and-panel-proxy.md.
|
|
11
|
-
#
|
|
12
|
-
# What this adds to the panel, and nothing else
|
|
13
|
-
# ----------------------------------------------
|
|
14
|
-
# host.docker.internal a name for the host. Docker Desktop resolves it on
|
|
15
|
-
# its own; on Linux `host-gateway` maps it to the
|
|
16
|
-
# bridge gateway, which is where the daemon must bind
|
|
17
|
-
# (PORTTA_HOST_BIND=172.17.0.1, or the address of the
|
|
18
|
-
# bridge in use). The daemon's loopback default is not
|
|
19
|
-
# reachable from a container on Linux.
|
|
20
|
-
# state/host/token the daemon's token, read-only. `portta up` and
|
|
21
|
-
# `portta web up` create it first: a bind mount of a
|
|
22
|
-
# file that does not exist would become a directory.
|
|
23
|
-
#
|
|
24
|
-
# The panel never receives a project directory, a socket or the daemon's
|
|
25
|
-
# database: only a URL and a secret, and every request it forwards is checked
|
|
26
|
-
# against a Portta permission first.
|
|
6
|
+
# One variable, because that is all this module needs now: the connection to
|
|
7
|
+
# the host daemon moved to `panel-host.yaml`, which the panel always gets, and
|
|
8
|
+
# what is left here is the switch that decides whether the panel mounts Task
|
|
9
|
+
# Flow's routes and sockets at all (ADR 0046).
|
|
27
10
|
# ============================================================================
|
|
28
11
|
|
|
29
12
|
services:
|
|
30
13
|
web:
|
|
31
|
-
extra_hosts:
|
|
32
|
-
- "host.docker.internal:host-gateway"
|
|
33
14
|
environment:
|
|
34
15
|
PORTTA_TASKFLOW: ${PORTTA_TASKFLOW:-false}
|
|
35
|
-
PORTTA_HOST_URL: ${PORTTA_HOST_URL:-http://host.docker.internal:${PORTTA_HOST_PORT:-5111}}
|
|
36
|
-
PORTTA_RUNTIME_HOST_TOKEN_FILE: /app/state/host/token
|
|
37
|
-
volumes:
|
|
38
|
-
- ./state/host/token:/app/state/host/token:ro
|
|
@@ -118,11 +118,10 @@ services:
|
|
|
118
118
|
# right just when the host uid happens to be 1000.
|
|
119
119
|
user: ${PORTTA_WEB_USER:-node}
|
|
120
120
|
environment:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
PORTTA_RUNTIME_DATABASE_URL: ${PORTTA_RUNTIME_DATABASE_URL:-}
|
|
121
|
+
# The panel's database is a file under $PORTTA_HOME, bind-mounted below.
|
|
122
|
+
# There is nothing to configure and no service to wait for: the file is
|
|
123
|
+
# created on first open (ADR 0037).
|
|
124
|
+
PORTTA_RUNTIME_DATABASE_FILE: /app/state/panel/portta.db
|
|
126
125
|
PORTTA_AUTH_SIGNIN_ATTEMPTS: ${PORTTA_AUTH_SIGNIN_ATTEMPTS:-}
|
|
127
126
|
ACME_CHALLENGE: ${ACME_CHALLENGE:-dns}
|
|
128
127
|
ACME_DNS_RESOLVERS: ${ACME_DNS_RESOLVERS:-}
|
|
@@ -209,24 +208,6 @@ services:
|
|
|
209
208
|
PUBLIC_DOMAIN: ${PUBLIC_DOMAIN:-}
|
|
210
209
|
CLOUDFLARE_ENABLED: ${CLOUDFLARE_ENABLED:-false}
|
|
211
210
|
CLOUDFLARE_ZONE: ${CLOUDFLARE_ZONE:-}
|
|
212
|
-
# The panel's only outbound integration, and its only long-lived secret
|
|
213
|
-
# that is not a Traefik credential. Off by default: with this false the
|
|
214
|
-
# panel makes no request to github.com. The private key is passed as a
|
|
215
|
-
# PATH, never as a value, because the panel can write this .env and must
|
|
216
|
-
# not be able to write the key that authenticates it.
|
|
217
|
-
#
|
|
218
|
-
# The path is the operator's: the Settings field writes it here, and this
|
|
219
|
-
# is the value the panel reads. What is fixed is the DIRECTORY, because
|
|
220
|
-
# `./state/github` below is the only place the key is mounted from; the
|
|
221
|
-
# filename is whatever GitHub called the .pem. The panel refuses a path
|
|
222
|
-
# outside that directory on save, and `portta doctor` fails on one.
|
|
223
|
-
# See docs/product/guides/github.md and docs/product/concepts/security.md.
|
|
224
|
-
GITHUB_APP_ENABLED: ${GITHUB_APP_ENABLED:-false}
|
|
225
|
-
GITHUB_APP_ID: ${GITHUB_APP_ID:-}
|
|
226
|
-
GITHUB_APP_PRIVATE_KEY_FILE: ${GITHUB_APP_PRIVATE_KEY_FILE:-/app/state/github/app.pem}
|
|
227
|
-
GITHUB_APP_WEBHOOK_SECRET: ${GITHUB_APP_WEBHOOK_SECRET:-}
|
|
228
|
-
GITHUB_API_URL: ${GITHUB_API_URL:-https://api.github.com}
|
|
229
|
-
GITHUB_SYNC_INTERVAL_MINUTES: ${GITHUB_SYNC_INTERVAL_MINUTES:-15}
|
|
230
211
|
volumes:
|
|
231
212
|
# Explicit paths from the host, and nothing else. No project directory is ever
|
|
232
213
|
# mounted here: see docs/development/adr/0001-decoupled-infrastructure.md.
|
|
@@ -237,7 +218,7 @@ services:
|
|
|
237
218
|
# state/environment host readiness report from the same collector
|
|
238
219
|
# state/runner the `{ verb, project }` request the runner reads
|
|
239
220
|
# state/access tunnel records cleaned when a project is removed
|
|
240
|
-
# state/
|
|
221
|
+
# state/panel the panel's SQLite database, read-write and its own
|
|
241
222
|
# state/auth credentials the panel manages, never served directly
|
|
242
223
|
# state/ssh SSH credentials the panel manages, never served directly
|
|
243
224
|
# traefik/dynamic read-write, and the ONE place the panel configures
|
|
@@ -252,8 +233,11 @@ services:
|
|
|
252
233
|
- ./state/environment:/app/state/environment:ro
|
|
253
234
|
- ./state/runner:/app/state/runner
|
|
254
235
|
- ./state/access:/app/state/access
|
|
255
|
-
# The
|
|
256
|
-
|
|
236
|
+
# The panel's own database. A directory rather than the file, because
|
|
237
|
+
# SQLite in WAL mode is three files and creates two of them itself — a
|
|
238
|
+
# bind mount of one path would leave the -wal and -shm inside the
|
|
239
|
+
# container, which is how a restart loses the most recent writes.
|
|
240
|
+
- ./state/panel:/app/state/panel
|
|
257
241
|
- ./state/auth:/app/state/auth
|
|
258
242
|
- ./state/ssh:/app/state/ssh
|
|
259
243
|
- ./config/traefik/dynamic:/app/state/traefik-dynamic
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codions/portta",
|
|
3
|
-
"version": "0.0.0-develop.
|
|
3
|
+
"version": "0.0.0-develop.150.sha-ed65081",
|
|
4
4
|
"description": "Machine-first CLI for a shared Docker development gateway",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@clack/prompts": "^1.1.0",
|
|
42
42
|
"@devcontainers/cli": "0.89.0",
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
44
|
-
"better-sqlite3": "^
|
|
44
|
+
"better-sqlite3": "^12.11.1",
|
|
45
45
|
"commander": "14.0.2",
|
|
46
46
|
"execa": "9.6.1",
|
|
47
47
|
"pi-acp": "^0.0.33",
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# ============================================================================
|
|
2
|
-
# Portta: panel persistence (always, with the panel)
|
|
3
|
-
# ============================================================================
|
|
4
|
-
# PostgreSQL is a private implementation detail of the panel, and a required
|
|
5
|
-
# one: the panel refuses to start without it, so this overlay is selected
|
|
6
|
-
# wherever web.yaml is (packages/core/src/config.ts). It has no host port, never
|
|
7
|
-
# joins the shared HTTP network, and keeps its data in a named volume that
|
|
8
|
-
# `portta down` does not remove — only `portta reset` does.
|
|
9
|
-
# ============================================================================
|
|
10
|
-
|
|
11
|
-
networks:
|
|
12
|
-
webdata:
|
|
13
|
-
name: ${PORTTA_DB_NETWORK:-portta-data}
|
|
14
|
-
internal: true
|
|
15
|
-
labels:
|
|
16
|
-
portta.managed: "true"
|
|
17
|
-
portta.component: data-network
|
|
18
|
-
|
|
19
|
-
volumes:
|
|
20
|
-
portta-db:
|
|
21
|
-
name: ${PORTTA_DB_VOLUME:-portta-db}
|
|
22
|
-
labels:
|
|
23
|
-
portta.managed: "true"
|
|
24
|
-
portta.component: db-volume
|
|
25
|
-
|
|
26
|
-
services:
|
|
27
|
-
db:
|
|
28
|
-
image: postgres:18.6-alpine
|
|
29
|
-
restart: unless-stopped
|
|
30
|
-
environment:
|
|
31
|
-
POSTGRES_DB: ${PORTTA_RUNTIME_DB_NAME:-portta}
|
|
32
|
-
POSTGRES_USER: ${PORTTA_RUNTIME_DB_USER:-portta}
|
|
33
|
-
# An empty value makes PostgreSQL refuse to initialise; it never makes it
|
|
34
|
-
# trust unauthenticated clients. bootstrap/web up normally fill this.
|
|
35
|
-
POSTGRES_PASSWORD: ${PORTTA_RUNTIME_DB_PASSWORD:-}
|
|
36
|
-
volumes:
|
|
37
|
-
# PostgreSQL 18+ stores data in a major-version subdirectory. Mount the
|
|
38
|
-
# parent so upgrades fail explicitly instead of creating nested data.
|
|
39
|
-
- portta-db:/var/lib/postgresql
|
|
40
|
-
networks:
|
|
41
|
-
- webdata
|
|
42
|
-
healthcheck:
|
|
43
|
-
test: ["CMD-SHELL", 'pg_isready -h 127.0.0.1 -p 5432 -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
|
|
44
|
-
interval: 5s
|
|
45
|
-
timeout: 3s
|
|
46
|
-
retries: 12
|
|
47
|
-
start_period: 5s
|
|
48
|
-
labels:
|
|
49
|
-
traefik.enable: "false"
|
|
50
|
-
portta.managed: "true"
|
|
51
|
-
portta.component: db
|
|
52
|
-
# No `ports:`. Only the panel can reach this service.
|
|
53
|
-
|
|
54
|
-
web:
|
|
55
|
-
environment:
|
|
56
|
-
PORTTA_DB_NETWORK: ${PORTTA_DB_NETWORK:-portta-data}
|
|
57
|
-
networks:
|
|
58
|
-
- webdata
|
|
59
|
-
# The panel exits if Postgres is not accepting connections yet. Compose
|
|
60
|
-
# merges this with the socket-proxy wait in web.yaml.
|
|
61
|
-
depends_on:
|
|
62
|
-
db:
|
|
63
|
-
condition: service_healthy
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# ============================================================================
|
|
2
|
-
# Portta: the one panel path that authenticates itself
|
|
3
|
-
# ============================================================================
|
|
4
|
-
# Applied when GITHUB_APP_ENABLED=true and the panel is routed on a domain.
|
|
5
|
-
#
|
|
6
|
-
# The problem
|
|
7
|
-
# -----------
|
|
8
|
-
# A GitHub App delivers webhooks to a URL of yours. Every panel path sits
|
|
9
|
-
# behind ForwardAuth, which expects a session cookie or a Basic credential --
|
|
10
|
-
# GitHub sends neither, so a delivery is refused before the panel ever sees it.
|
|
11
|
-
# The panel cannot be told about a push, an issue, or a new installation.
|
|
12
|
-
#
|
|
13
|
-
# Why an exemption is safe here, and only here
|
|
14
|
-
# --------------------------------------------
|
|
15
|
-
# This is not a hole in the panel's authentication. It is one path that carries
|
|
16
|
-
# a *different* and, for a machine-to-machine callback, stronger one: GitHub
|
|
17
|
-
# signs the raw body with HMAC-SHA256 under a secret only it and this host
|
|
18
|
-
# know, and `verifySignature` compares in constant time, refusing a missing
|
|
19
|
-
# secret, a missing header and a wrong digest alike. Nothing is parsed before
|
|
20
|
-
# that check passes.
|
|
21
|
-
#
|
|
22
|
-
# So the request is authenticated, just not by a cookie. A session cookie would
|
|
23
|
-
# be the wrong instrument: GitHub has no session, and any scheme that let it in
|
|
24
|
-
# by origin or by address would trust something forgeable.
|
|
25
|
-
#
|
|
26
|
-
# What this deliberately is not
|
|
27
|
-
# -----------------------------
|
|
28
|
-
# It is not a general "these URLs are public" list. Every other panel path
|
|
29
|
-
# authenticates by session and by nothing else, so exempting one would be an
|
|
30
|
-
# unauthenticated door into an API that can start, stop and remove containers.
|
|
31
|
-
# This router names one exact path with `Path(...)`, not a prefix -- there is
|
|
32
|
-
# no `/api/integrations/github/webhook/../projects` to walk out of.
|
|
33
|
-
#
|
|
34
|
-
# The priority is above the panel's own router so the exact path wins over
|
|
35
|
-
# `Host(...)`; both name the same service, so the panel process is unchanged.
|
|
36
|
-
# ============================================================================
|
|
37
|
-
|
|
38
|
-
services:
|
|
39
|
-
web:
|
|
40
|
-
labels:
|
|
41
|
-
traefik.enable: "true"
|
|
42
|
-
traefik.http.routers.portta-panel-webhook.rule: >-
|
|
43
|
-
Host(`${PORTTA_PANEL_ADVERTISED_HOST:-portta-web}`) && Path(`/api/integrations/github/webhook`)
|
|
44
|
-
traefik.http.routers.portta-panel-webhook.priority: "20000"
|
|
45
|
-
traefik.http.routers.portta-panel-webhook.service: portta-panel
|
|
46
|
-
traefik.http.services.portta-panel.loadbalancer.server.port: "8081"
|