Merge commit 'ad15366f3dc02480ede74dce80a074c190e83d3c'

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-28 16:07:25 +01:00
10 changed files with 57 additions and 17 deletions
+1
View File
@@ -216,6 +216,7 @@ Pulse is secure by default. On first launch, you must retrieve a **Bootstrap Tok
| Platform | Command |
|----------|---------|
| **Docker** | `docker exec pulse /app/pulse bootstrap-token` |
| **Docker app UIs** (Unraid, Portainer, TrueNAS apps) | Open the Pulse container's console and run `/app/pulse bootstrap-token` |
| **Kubernetes** | `kubectl exec -it <pod> -- /app/pulse bootstrap-token` |
| **Systemd** | `sudo pulse bootstrap-token` |
| **Proxmox LXC** | `pct exec <ctid> -- /usr/local/bin/pulse bootstrap-token` (run on the Proxmox host; the installer prints this command with your container ID at the end of the install) |
@@ -2057,6 +2057,11 @@ plaintext secret file on disk. Canonical runtime persistence must keep the
token encrypted at rest, and any legacy plaintext bootstrap-token file must be
treated only as migration input that is rewritten immediately into the
encrypted canonical format on load.
Operator guidance for recovering that token, in the startup log and on the
first-run unlock screen, must name the supported command and never the token
file, because the file holds ciphertext. The unlock screen lists the host,
`docker exec`, in-container console (`/app/pulse bootstrap-token`) and
`pct exec` forms without revealing the server's deployment details.
Managed first-session proof may reset that boundary only through the dev-only
`/api/security/dev/reset-first-run` route under authenticated
`settings:write`; harnesses may not scrape `.env`, delete persisted token
+31 -16
View File
@@ -1,28 +1,43 @@
{
"version": 1,
"base_sha": "6d7e3342e56a4f8f2b411acb3df8dc7777d4ec7f",
"verified_at": "2026-09-27T23:52:13Z",
"base_sha": "75c72ef505b24e31214162b7b46f97e357528ae3",
"verified_at": "2026-09-28T14:14:00Z",
"result": "passed",
"changed_paths": ["frontend-modern/src/api/settings.ts"],
"changed_paths": [
"frontend-modern/src/components/SetupWizard/steps/WelcomeStep.tsx",
"frontend-modern/src/i18n/messages.de.ts",
"frontend-modern/src/i18n/messages.es.ts",
"frontend-modern/src/i18n/messages.ts"
],
"content_sha256": {
"frontend-modern/src/api/settings.ts": "4172b94c268be6a0ea000e73d155e540f11776e1c59325250e13716722be5aeb"
"frontend-modern/src/components/SetupWizard/steps/WelcomeStep.tsx": "cb25935ec49782592c1602ae1502f5becc94108d61c3a786bbd65654d757e540",
"frontend-modern/src/i18n/messages.de.ts": "ca6bd4a42cac62c3bdec6ef25c35b5ec78b886d0455c6eab0ae8f154c0fd3361",
"frontend-modern/src/i18n/messages.es.ts": "5b3867658b39164afdece84e7a1eb28195b18d810d9ca9f5ed6761a54e274dcd",
"frontend-modern/src/i18n/messages.ts": "9e0bd2b4addcf984e499499460fee59f705d684cffb4fba1d7da0361cfbbdd31"
},
"routes": ["/tmp/telemetry-schema-2285.html"],
"routes": [
"/ (first-run unlock screen, fresh container)"
],
"viewports": [
{"width": 1280, "height": 800},
{"width": 390, "height": 844}
{
"width": 1280,
"height": 800
},
{
"width": 390,
"height": 844
}
],
"states": [
"Production GeneralSettingsPanel mounted in an isolated Vite fixture with a synthetic schema-v18 telemetry-preview API response",
"Preview payload displayed stable-channel skipped, metadata_error, and available outcomes with corresponding false, false, and true update_available booleans",
"The payload block stayed inside the card; the metadata_error JSON scrolled within the block at 390px without page-level horizontal overflow",
"Desktop and phone screenshots for all three outcomes were inspected for placement, clipping and scrolling"
"Fresh rcourtman/pulse:latest (v6.4.1) container in first-run state, serving this branch's vite build via PULSE_FRONTEND_DIR, reached through an SSH tunnel to localhost",
"Unlock setup lists four rows: direct host, Docker deployment, the new Container console (Unraid, Portainer, TrueNAS apps) row with /app/pulse bootstrap-token, and LXC",
"At 390px the new label wraps to two lines and the command truncates like the existing rows, with no page-level horizontal overflow (scrollWidth 390)",
"At 1280px all four commands render in full with no overflow"
],
"interactions": [
"Clicked Preview payload and then Refresh payload twice per viewport, replacing the synthetic API outcome on each request",
"Parsed the rendered payload JSON and asserted schema_version, update_channel, update_check_outcome and update_available",
"Checked page and preview scroll widths at both viewports and checked for uncaught page errors"
"Clicked the copy button on the Container console row and saw the Command copied to clipboard toast and the check icon",
"Ran /app/pulse bootstrap-token from a shell inside the container as root and as the pulse user and confirmed it printed the token"
],
"command": "pulse-worker-browser tmp/browser-proof/telemetry-schema-2285.cjs (Playwright 1.56.1, Chromium 141.0.7390.37)",
"notes": "Offline production-component fixture with synthetic API data, not a full application login, installed sender, receiver or release acceptance. The browser result and six screenshots are retained under tmp/browser-proof in this assigned workspace."
"command": "Claude desktop browser pane against a pulse-dev probe container",
"notes": "Backend was the published v6.4.1 image, not a build of this branch. The frontend is this branch. Telemetry was disabled on the probe containers."
}
+1
View File
@@ -216,6 +216,7 @@ Pulse is secure by default. On first launch, you must retrieve a **Bootstrap Tok
| Platform | Command |
|----------|---------|
| **Docker** | `docker exec pulse /app/pulse bootstrap-token` |
| **Docker app UIs** (Unraid, Portainer, TrueNAS apps) | Open the Pulse container's console and run `/app/pulse bootstrap-token` |
| **Kubernetes** | `kubectl exec -it <pod> -- /app/pulse bootstrap-token` |
| **Systemd** | `sudo pulse bootstrap-token` |
| **Proxmox LXC** | `pct exec <ctid> -- /usr/local/bin/pulse bootstrap-token` (run on the Proxmox host; the installer prints this command with your container ID at the end of the install) |
@@ -117,6 +117,10 @@ describe('WelcomeStep', () => {
expect(
screen.getByText('docker exec <pulse-container> /app/pulse bootstrap-token'),
).toBeInTheDocument();
expect(
screen.getByText('Container console (Unraid, Portainer, TrueNAS apps)'),
).toBeInTheDocument();
expect(screen.getByText('/app/pulse bootstrap-token')).toBeInTheDocument();
// Absolute path, because pct exec runs with
// PATH=/sbin:/bin:/usr/sbin:/usr/bin and cannot resolve /usr/local/bin.
expect(
@@ -37,6 +37,13 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
label: t('setup.welcome.deploymentLabel.docker'),
command: 'docker exec <pulse-container> /app/pulse bootstrap-token',
},
{
// Unraid, Portainer and TrueNAS open a shell inside the container
// itself, so the docker exec prefix above does not apply there.
id: 'container-console',
label: t('setup.welcome.deploymentLabel.containerConsole'),
command: '/app/pulse bootstrap-token',
},
{
id: 'lxc',
label: t('setup.welcome.deploymentLabel.lxc'),
+2
View File
@@ -317,6 +317,8 @@ export const DE_MESSAGE_OVERRIDES = {
'Pulse scheint in Docker zu laufen. Fuehren Sie den Befehl auf dem Docker-Host aus und ersetzen Sie <pulse-container> durch den Namen des laufenden Pulse-Containers.',
'setup.welcome.deploymentHint.lxc':
'Pulse scheint in LXC-Container {ctid} zu laufen. Fuehren Sie den Befehl auf dem Proxmox-Host aus, um in diesen Container zu wechseln und das einmalige Setup-Token auszugeben.',
'setup.welcome.deploymentLabel.containerConsole':
'Container-Konsole (Unraid, Portainer, TrueNAS-Apps)',
'setup.welcome.deploymentLabel.containerized': 'Containerisierte Bereitstellung',
'setup.welcome.deploymentLabel.direct': 'Direkte Host-Installation',
'setup.welcome.deploymentLabel.docker': 'Docker-Bereitstellung',
+2
View File
@@ -309,6 +309,8 @@ export const ES_MESSAGE_OVERRIDES = {
'Pulse parece ejecutarse en Docker. Ejecuta el comando en el host Docker y reemplaza <pulse-container> por el nombre del contenedor Pulse en ejecución.',
'setup.welcome.deploymentHint.lxc':
'Pulse parece ejecutarse en el contenedor LXC {ctid}. Ejecuta el comando en el host Proxmox para entrar en ese contenedor e imprimir el token de configuración de un solo uso.',
'setup.welcome.deploymentLabel.containerConsole':
'Consola del contenedor (Unraid, Portainer, apps de TrueNAS)',
'setup.welcome.deploymentLabel.containerized': 'Despliegue contenedorizado',
'setup.welcome.deploymentLabel.direct': 'Instalación directa en host',
'setup.welcome.deploymentLabel.docker': 'Despliegue Docker',
+3
View File
@@ -301,6 +301,8 @@ export const EN_MESSAGES = {
'Pulse appears to be running in Docker. Run the command on the Docker host and replace <pulse-container> with the running Pulse container name.',
'setup.welcome.deploymentHint.lxc':
'Pulse appears to be running in LXC container {ctid}. Run the command on the Proxmox host to execute into that container and print the one-time setup token.',
'setup.welcome.deploymentLabel.containerConsole':
'Container console (Unraid, Portainer, TrueNAS apps)',
'setup.welcome.deploymentLabel.containerized': 'Containerized deployment',
'setup.welcome.deploymentLabel.direct': 'Direct host install',
'setup.welcome.deploymentLabel.docker': 'Docker deployment',
@@ -694,6 +696,7 @@ export const FIRST_SESSION_MONITORING_MIGRATED_MESSAGE_KEYS = [
'setup.welcome.deploymentHint.dockerNamed',
'setup.welcome.deploymentHint.dockerUnnamed',
'setup.welcome.deploymentHint.lxc',
'setup.welcome.deploymentLabel.containerConsole',
'setup.welcome.deploymentLabel.containerized',
'setup.welcome.deploymentLabel.direct',
'setup.welcome.deploymentLabel.docker',
+1 -1
View File
@@ -62,7 +62,7 @@ func (r *Router) initializeBootstrapToken() {
if created {
logger.Warn().
Str("token_path", path).
Msg("Bootstrap setup token created on disk; reveal it locally with `pulse bootstrap-token` or by reading the token file path")
Msg("Bootstrap setup token created; the file is encrypted, so reveal the token with `pulse bootstrap-token` (Docker: `docker exec <container> /app/pulse bootstrap-token`, or `/app/pulse bootstrap-token` from the container console)")
} else {
logger.Info().
Str("token_path", path).