docs: v1 docs refresh (batch 5) (#1395)
* docs(introduction): refresh for the redesigned UI and replace screenshots Bring the Getting Started Introduction page in line with the current product: - Add the Security top-level view to the navigation list and a dedicated Security section with a new screenshot. - Correct the Fleet tab names (Snapshots, Status, Map, Deployments, Routing, Federation, Actions, Secrets). - Split Settings out from security and list the current nine setting groups (Security graduated to its own view). - Refine the navigation paragraph so role, tier, and local-vs-remote context read accurately. Replace all four existing screenshots (Home, stack workspace, Fleet, Resources) with fresh captures of the redesigned UI and add a Security overview screenshot. * docs(configuration): document advanced env vars and clarify deployment vs runtime config Add an Advanced environment variables section (TRIVY_BIN, SENCHO_MESH_SUBNET, GITSOURCE_MAX_CLONE_BYTES, SENCHO_PUBLIC_URL, SENCHO_COMPOSE_STALL_TIMEOUT_MS) and reframe the intro to separate deployment-time configuration from the runtime settings that live in the in-app Settings Hub. Cross-link the pilot-agent variables to the Pilot Agent page instead of duplicating them. * docs(sso): refresh SSO Setup Guide and SSO & LDAP reference for the redesigned UI Refresh both SSO documentation pages against the current product and the redesigned settings UI. - Correct the navigation path to Settings -> Access -> SSO on both pages. - Fix the "Require 2FA on SSO sign-in" toggle location to Settings -> Personal -> Account. - Describe the login-page experience (the Local / LDAP toggle and the branded OIDC buttons under the "Or continue with" divider) and the SSO panel masthead (SCOPE, PROVIDERS, ENABLED). - Replace all six SSO screenshots with fresh captures of the redesigned UI. * docs(features): refresh the Features Overview page for the redesigned UI Rewrite docs/features/overview.mdx to mirror the current Features navigation grouping (Stacks, Deployment, Resources, Observability, Fleet, Automation, Security & Identity) and add the recently shipped capabilities surfaced in the redesign: Stack Dossier, Drift Detection, Compose Doctor, Compose Networking, Environment & secrets guardrails, Storage portability, Health-Gated Updates, Fleet Dossier, and the dedicated Security page. Correct stale claims (the file explorer now gates writes on stack edit permission, not an admin role; downloads are a read action; bulk label assign now spans nodes) and standardize the tier callouts so partly paid features read as "Admiral adds X". Replace the three pre-redesign screenshots and add a Security overview banner, all captured from a populated fleet. * docs(features): refresh the Appearance page for the redesigned UI Add fresh screenshots and a troubleshooting section to the Appearance page, verified against the live product. - Add four screenshots: the Theme card (live preview, mode, accent, and fine-tune sliders), the top-bar quick switcher, the Typography card, and the Display card. - Refresh the Density screenshot used by the Settings reference page. - State that the quick switcher also covers text size, and that the contrast, border, and glow sliders stay in Settings. - Add a Troubleshooting accordion covering per-browser persistence, resets to defaults, cross-operator scope, and the quick-switcher versus full-Settings split. * docs(introduction): refresh screenshots and correct stale content * docs(reference): refresh the Settings Reference page for the redesigned UI Replace all seven stale screenshots with fresh 1920x1080 captures. Add five new screenshots for the sections that previously had none. Content changes: - Sidebar table: rename Infrastructure "Fleet Mesh" entry to "Fleet"; add "Image update checks" to the Automation group list - Fleet section: rename heading to match registry label; add the Documentation snapshots subsection (snapshot_documentation toggle) - Container Alerts: add screenshot - Image update checks: add the full section (Registry checks table, scheduling mode, interval presets, cron expression support) - Stacks / Deploy Guardrails: add screenshot - Recovery: add the full section (System health snapshot, Environment preflight checks, Safe actions, Command-line recovery table) * docs(sso): refresh screenshots for SSO quickstart and feature pages * docs: refresh Features Overview screenshots and content Replace all 4 hero screenshots with fresh 1920x1080 production captures. Correct security posture state names (Action needed / Monitoring / Secure), add the Policies tab to the Security section tab list, mention the Simple mode in Scheduled operations, and update all alt text to match the new screenshots. * docs: refresh Appearance page screenshots and correct quick-switcher scope Replace all four Appearance screenshots with fresh production captures. Fix the quick-switcher control list: remove fonts (not present in the popover), add visual style and readability which are. Add Log chip color to the Display section. Update all screenshot alt text to match new captures. * docs: refresh stack management page with current UI and anatomy tabs * docs: fix convert-tab-error screenshot with fully visible error toast * docs: convert troubleshooting section to AccordionGroup format * docs(quickstart): refresh screenshots and align dashboard description Replace all three first-boot and dashboard screenshots with current UI. Add Security to the top navigation list, update gauge and Stack health descriptions to reflect sparklines and column detail, and align Configuration Status wording with the Introduction page. * docs(editor): rewrite anatomy panel, replace all screenshots - Correct the anatomy panel tab inventory: the panel has eight tabs (Anatomy, Activity, Dossier, Drift always; Environment, Networking, Doctor, Storage when the node advertises the matching capability), not three as previously documented - Add table describing all eight tabs with capability gates and links to dedicated feature pages - Add anatomy-tabs.png screenshot showing the scrollable tab row - Note the Doctor severity dot (red for blocker, amber for high-risk) - Remove the stale Markdown-export subsection; Dossier and Activity are now covered in the tab table - Replace all six stale screenshots with fresh 1920x1080 captures - Replace the compose diff preview screenshot * docs(files): refresh Files & Volumes screenshots and fix context-menu alt text Replace all 9 stale screenshots on the Files & Volumes page with fresh captures from the production node. Fix three alt-text strings that did not match the live UI: removed hardcoded octal value 644, and added the Duplicate, Copy to, and Move to entries missing from the context-menu alt text. * docs: rewrite Stack Activity page with full event categories and fresh screenshots Expands the event category table from 5 to 10 entries to cover drift detected, drift resolved, update started, health gate passed, and health gate failed. Adds a live-disconnected-state section, a background-actor attribution table, and a corrected troubleshooting accordion covering the WebSocket reconnect case. Replaces both stale screenshots with fresh 1920x1080 captures from the production node. * docs(drift): rewrite drift detection page with screenshots and full coverage Full rewrite of the Drift Detection feature page. Adds two previously undocumented finding types (network-undeclared, network-missing), expands the temporal section to distinguish the raw-file hash from the parsed-model hash, documents the two-layer spatial-engine and ledger architecture, explains when the ledger is reconciled (post-deploy vs manual re-check vs tab open), adds Activity timeline integration note, introduces a Limitations section (no background scanner, port-range caveat, history cap, advisory-only enforcement), expands Troubleshooting from five entries to seven using the AccordionGroup convention, and adds four production screenshots. * docs(drift): use CardGroup for Related section * docs(dossier): rewrite Stack Dossier page with full feature coverage * docs(networking): rewrite Compose Networking page with full feature coverage * docs(doctor): rewrite Compose Doctor with full 30-rule reference, screenshots, and cross-links * docs(networking): add production screenshots and correct alt text Adds 7 production screenshots for all sections of the Compose Networking page and updates the four placeholder alt texts written before screenshots were taken to match what the actual images show (arr-net external badge, swag service with 443/tcp and 80/tcp, single-service exposure intent row). Also adds the full-panel overview image at the top of the page. * docs(environment-guardrails): rewrite with project env file, env file status, and screenshots * docs(storage): rewrite Storage Portability page with screenshots and full coverage Rewrites compose-storage.mdx from a 61-line sketch into a complete reference page. Key additions: Where to find it section with screenshot, full storage inventory section documenting all mount type/access/status chips and the Linux owner display, expanded portability verdict section with per-reason detail and edge-case caveats (read-only binds, symlink escapes, anonymous volume risks), snapshot coverage section with admin scope and remote-node behavior, Findings in Doctor cross-reference, and six troubleshooting accordions covering tab visibility, bind status, external named volumes, render errors, and snapshot coverage states. Adds two production screenshots: storage-tab.png and storage-node-bound.png. * docs(stack-labels): rewrite with accurate permissions, capability gate, dry run, live preview, and color conflict docs * docs: rewrite Stack Sidebar page with accurate feature coverage Rewrites the Stack Sidebar documentation page to match the current UI. Key changes: - Fix branding header description (shows logo + version, not just version) - Fix bulk mode icon description (stacked-rows, not square) - Add cross-node search section (fan-out behavior, Other nodes section, unreachable-node warnings, click-to-switch navigation) - Update Labels submenu description (inline New label creation, Manage labels link) - Note that Delete only appears when the user has delete permission - Remove the auto-update implication from Schedule task description - Rewrite the Activity ticker section with the full 6-state priority cascade table; remove the non-existent IDLE state; correct pulsing-dot behavior - Replace all 7 stale screenshots with fresh production screenshots - Add new sidebar-cross-node-search.png screenshot * docs(atomic-deployments): refresh screenshot and document project env files, rollback readiness, and recovery actions * docs(atomic-deployments): fix rollback permission visibility and banner string accuracy The Rollback menu entry is hidden by the frontend when the user lacks stack:deploy; it never appears and does not 403. Fixed the step-4 narrative and troubleshooting accordion to match. The rollback-failure banner emitted by ComposeService is '=== Rollback failed. Manual intervention may be required ===' (period, capital M). Fixed both occurrences in the page. Updated the Settings navigation path from the nonexistent 'Roles & Access' to the real 'Access'. * docs(deploy-progress): rewrite with health gate, inline style, and 9 fresh screenshots Add health gate section covering all four states (observing, passed, failed, unknown) with exact UI banner text and the configurable observation window. Expand the inline style section with full band content, 4s auto-dismiss, and pill handoff. Add Scanning as a supported entry point. Replace all 6 existing screenshots and add 3 new ones (modal-health-gate, inline-banner, setting-style). Add two health gate troubleshooting accordions. Add Related CardGroup linking to health-gated-updates, stack-activity, deploy-enforcement, and atomic-deployments. * docs(health-gated-updates): refresh screenshots and correct signal row order and label * docs(deploy-enforcement): rewrite with fleet replication, honor suppressions location, scan-failed dialog state, and fresh screenshots Adds the Fleet policy replication section covering control/replica behavior, Managed by control node banner, and Demote to control. Documents the exact location of the Honor suppressions toggle (bottom of Policies tab). Expands the block dialog section with the scan-failed row state. Updates all three screenshots to the current visual design. Restores the Admiral license note and corrects the policy-card scope description. * docs(app-store): rewrite with mobile layout, fresh screenshots, and registry admin note - Replace all 5 stale screenshots with 1920x1080 production captures - Add app-store-mobile.png showing the status masthead layout - Document mobile single-column layout in a new Mobile subsection - Note that the featured hero has its own Deploy button - Mark the category rail as desktop only with a cross-link to Mobile - Add admin-account requirement to the custom registry section - Add Related CardGroup linking vulnerability scanning, deploy progress, deploy enforcement, and resources
@@ -19,11 +19,11 @@ The search bar at the top filters tiles by name, description, or category in rea
|
||||
|
||||
### Categories rail
|
||||
|
||||
The left rail lists every category the registry exposes, plus an **All** entry, with a count for each. The active category gets a brand-coloured rail and tinted background. The rail appears once at least one template has loaded.
|
||||
The left rail lists every category the registry exposes, plus an **All** entry, with a count for each. The active category gets a brand-coloured rail and tinted background. The rail appears once at least one template has loaded. The category rail is desktop only; see [Mobile](#mobile) for the small-screen layout.
|
||||
|
||||
### Featured template
|
||||
|
||||
A single template is pinned to the top of the grid in a wide editorial banner. Sencho rotates the featured pick **weekly** through the five most-starred templates in the registry, so the catalogue stays fresh without you doing anything. The banner disappears whenever you have a search query active.
|
||||
A single template is pinned to the top of the grid in a wide editorial banner. The banner includes its own **Deploy** button so you can ship the featured template without opening its tile. Sencho rotates the featured pick **weekly** through the five most-starred templates in the registry, so the catalogue stays fresh without you doing anything. The banner disappears whenever you have a search query active.
|
||||
|
||||
### Tiles
|
||||
|
||||
@@ -40,6 +40,14 @@ Each tile shows:
|
||||
|
||||
Tiles are sorted by GitHub star count, descending. Click any tile to open the deployment sheet on the right.
|
||||
|
||||
### Mobile
|
||||
|
||||
On small screens, the App Store adapts to a single-column layout. The masthead shows the store label, the total app count, the category count, and an action menu for navigation. Templates are listed alphabetically rather than by star count, and the category sidebar and featured hero are not shown on mobile.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/app-store/app-store-mobile.png" alt="App Store on a mobile device: status masthead showing APP STORE, 202 apps, and 12 categories, with an alphabetical single-column template list below" />
|
||||
</Frame>
|
||||
|
||||
## Deploying a template
|
||||
|
||||
The deployment sheet slides in from the right. The header carries a breadcrumb (`App store › <template>`), the template name in the cockpit display face, and a meta line that combines the supported architectures, the GitHub star count, and the target node name when you are deploying to a remote node.
|
||||
@@ -127,8 +135,12 @@ Sencho can stream the live `docker compose` output for every template install. O
|
||||
|
||||
By default Sencho uses LinuxServer.io's hosted catalogue. To point at your own:
|
||||
|
||||
<Note>
|
||||
Changing the registry URL requires an **admin** account.
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/app-store/app-store-settings-registry.png" alt="Settings · Infrastructure · App Store with two panels: Default registry showing LinuxServer.io and the api.linuxserver.io/api/v1/images URL, and Custom registry with a Registry URL input, a 'using default' hint, and Reset to default and Save & refresh buttons" />
|
||||
<img src="/images/app-store/app-store-settings-registry.png" alt="Settings › Infrastructure › App Store with two panels: Default registry showing LinuxServer.io and the api.linuxserver.io/api/v1/images URL, and Custom registry with a Registry URL input, a using-default hint, and Reset to default and Save and refresh buttons" />
|
||||
</Frame>
|
||||
|
||||
1. Open **Settings › Infrastructure › App Store**.
|
||||
@@ -155,3 +167,20 @@ The default registry display at the top of the page is informational only; you c
|
||||
The **Scan images for vulnerabilities after deploy** checkbox renders only when the active node reports that Trivy is installed. Configure scanning per the [Vulnerability Scanning](/features/vulnerability-scanning) page, then reopen the deployment sheet. Even with the checkbox missing, the rest of the Advanced tab works exactly the same; you just lose the post-deploy scan trigger.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Vulnerability Scanning" icon="shield-halved" href="/features/vulnerability-scanning">
|
||||
Understand the CVE badges that appear on every template tile and what they mean.
|
||||
</Card>
|
||||
<Card title="Deploy Progress" icon="chart-line" href="/features/deploy-progress">
|
||||
Stream live docker compose output for every App Store deploy.
|
||||
</Card>
|
||||
<Card title="Deploy Enforcement" icon="ban" href="/features/deploy-enforcement">
|
||||
Set risk policies that gate App Store deploys before the stack is created.
|
||||
</Card>
|
||||
<Card title="Resources Hub" icon="box-archive" href="/features/resources">
|
||||
View scan results for images pulled by App Store templates.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -5,7 +5,15 @@ description: Personalize Sencho's look. Choose a visual style, tune readability
|
||||
|
||||
**Settings → Appearance** is where you make Sencho look the way you want. Choose between the Calm and Signature visual styles, dial in readability, set how the security charts are colored, pick a theme and accent, choose the interface and data fonts, and scale the text. A live preview reflects every change as you make it.
|
||||
|
||||
Several of these controls are also one click away from anywhere in the app: the **palette button** in the top bar, between the search and notification icons, opens a quick switcher for the theme mode, accent, visual style, readability, and text size.
|
||||
<Frame>
|
||||
<img src="/images/settings/appearance-overview.png" alt="The Settings → Appearance page showing the Visual Style section with two preview cards: Calm (selected, with upright Critical heading and muted severity-color dots) and Signature (with italic serif Critical heading and saturated dots). Below are the Security Visualization chart-palette control with Muted selected, and the Readability section with its mode toggle, header-style control, and contrast slider." />
|
||||
</Frame>
|
||||
|
||||
Several of these controls are also one click away from anywhere in the app: the **palette button** in the top bar, between the search and notification icons, opens a quick switcher for the theme mode, accent, visual style, readability, and text size. The sliders for contrast, borders, and glow stay in this Settings section.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/settings/appearance-quick-switch.png" alt="The top-bar Theme quick-switcher popover: a Mode row with icon-only buttons for Dim, OLED, Light, and Auto (Dim selected); an eight-swatch Accent grid with Cyan selected; a Visual style row with Calm and Signature buttons (Calm selected) above a Readability toggle set to off; a Text size row with S, M, L, and XL preset buttons (M selected); and a footer line reading 'Saved to this browser · fine-tune borders & glow in Settings'." />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
Appearance choices are saved to **this browser only**. They follow you across tabs on the same browser, and every device remembers its own look. Nothing here changes what other operators see.
|
||||
@@ -57,6 +65,10 @@ The **accent** is Sencho's one data color. It drives charts, sparklines, progres
|
||||
|
||||
Sencho uses three type contexts. The interface and data faces are yours to change; the heading style follows your Visual style choice.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/settings/appearance-typography.png" alt="The Appearance Typography section with Interface font chips showing Ag sample text (Geist selected, IBM Plex, Hanken Grotesk) and Data font chips showing 01 sample text (Geist Mono selected, Plex Mono, Fira Code), with a Text size slider at 1.00×." />
|
||||
</Frame>
|
||||
|
||||
- **Interface font** sets the sans face used for body text, labels, navigation, and buttons. Choose **Geist** (default), **IBM Plex Sans**, or **Hanken Grotesk**.
|
||||
- **Data font** sets the monospace face used for the terminal, stat values, codes, and timestamps. Choose **Geist Mono** (default), **IBM Plex Mono**, or **Fira Code**.
|
||||
- **Text size** scales the entire interface from a single root multiplier. In Settings this is a continuous slider (default `1.00×`); the top-bar quick switcher offers **S / M / L / XL** presets. Both stay in sync.
|
||||
@@ -67,5 +79,27 @@ The **Display** group holds the remaining per-browser preferences:
|
||||
|
||||
- **Density** switches between **Comfortable** (roomy rows, the default) and **Compact** (tighter rows and tiles that fit more on screen for dense dashboards).
|
||||
- **Top navigation labels** shows text labels beside the top navigation icons. Turn it off for an icon-only bar; the destinations stay reachable by hover tooltip, accessible name, and the command palette. On the phone layout the navigation always keeps its labels. With labels off, **Top navigation alignment** chooses whether the icon-only bar sits to the left or centered.
|
||||
- **Log chip color** controls how service chips are colored in log views. **Unified** uses the accent color for all service chips. **Per service** assigns each service a stable label color for faster visual scanning when following multiple services at once.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/settings/appearance-display.png" alt="The Appearance Display section with a Density selector set to Comfortable, a Top navigation labels toggle turned off revealing a Top navigation alignment control with Left selected and Center, and a Log chip color control with Unified selected alongside Per service." />
|
||||
</Frame>
|
||||
|
||||
Deploy-progress behavior and the diff-preview-before-save step are stack workflow preferences, so they live in **Settings → Infrastructure → Stacks**, not here.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="My theme didn't carry over to another device or browser">
|
||||
Appearance is saved to the browser you set it in, not to your account. Every browser and every device keeps its own look on purpose, so a compact laptop setup never forces the same rhythm on a large desktop. Open Sencho on the new browser, pick your theme, accent, fonts, and density again, and they persist there from then on.
|
||||
</Accordion>
|
||||
<Accordion title="My appearance reset to the defaults">
|
||||
Sencho reads your choices from this browser's local storage. Clearing site data, signing in from a private or incognito window, or a browser that blocks local storage drops you back to the defaults: **Dim** mode, **Cyan** accent, **Geist** and **Geist Mono** fonts, **1.00×** text size, and **Comfortable** density. Re-pick what you want and it sticks for that browser.
|
||||
</Accordion>
|
||||
<Accordion title="Will changing my accent or theme affect other operators?">
|
||||
No. Appearance is local to your browser and is never sent to the server or shared. Other operators, and your own other devices, keep whatever look they set. There is no shared or organization-wide appearance setting; each person chooses their own.
|
||||
</Accordion>
|
||||
<Accordion title="The quick switcher doesn't have the contrast, border, and glow sliders">
|
||||
The top-bar palette button is a fast path for the most-used controls: mode, accent, visual style, readability, and text size. Contrast, border brightness, and ambient glow live here in **Settings → Appearance**, where the live preview reflects each change as you drag. Both paths write to the same per-browser storage, so a change in one shows up immediately in the other.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -3,18 +3,18 @@ title: Atomic Deployments
|
||||
description: Wrap every deploy and update in a backup, a 3-second health probe, and an automatic rollback when a container crashes.
|
||||
---
|
||||
|
||||
Sencho wraps every protected deploy in a four-step safety net: it copies the current `compose.yaml` and `.env` to a writable backup directory, runs the requested compose action, waits 3 seconds for the new containers to settle, then checks them for a non-zero exit code. If any container has crashed, Sencho restores the backed-up files and re-deploys automatically.
|
||||
Sencho wraps every protected deploy in a four-step safety net: it backs up the current compose file, `.env`, and any configured project env files, runs the compose action, waits 3 seconds for containers to settle, then checks for a non-zero exit code. If any container crashed, Sencho restores the backup and re-deploys automatically.
|
||||
|
||||
The same backup also powers the **Rollback** action in the stack editor, so you can roll a stack back to its last good configuration on demand. To see in advance whether that rollback would actually help, and to watch container health for longer than the 3-second probe, see [Health-Gated Updates](/features/health-gated-updates).
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Backup.** Before the action runs, Sencho copies `compose.yaml` (or `compose.yml` / `docker-compose.yaml` / `docker-compose.yml`) and `.env`, if present, into the backup directory. The deploy progress modal streams `=== Backup created for atomic deployment ===` once the copy completes, before any `docker compose` output.
|
||||
1. **Backup.** Before the action runs, Sencho copies `compose.yaml` (or `compose.yml` / `docker-compose.yaml` / `docker-compose.yml`), `.env` if present, and any project env files configured for the stack (for example, `stack.env` or `.env.production`) into the backup directory. The deploy progress modal streams `=== Backup created for atomic deployment ===` once the copy completes, before any `docker compose` output.
|
||||
2. **Run the action.** Sencho executes the requested compose action: `up -d` for a deploy, or a pull-then-`up -d` recreate for an update.
|
||||
3. **Health probe.** Sencho waits 3 seconds, then lists every container with the `com.docker.compose.project=<stack>` label and checks each one for a non-zero exit code. Any container that has exited with a non-zero status counts as a crash.
|
||||
4. **Auto-rollback on failure.** When a crash is detected, Sencho streams `=== Deployment failed - restoring previous compose and env files ===`, restores the backed-up files, and re-runs `docker compose up -d` with the restored configuration. On success it streams `=== Restored previous compose and env files ===`. Restoring the files reverts the compose and `.env` configuration; an image referenced by a moving tag (such as `latest`) is not reverted, because the local tag still resolves to the newly pulled digest. The original deploy error is preserved and reported as the deploy result, so a failed-then-rolled-back deploy still registers as a failure in the deploy progress modal.
|
||||
4. **Auto-rollback on failure.** When a crash is detected, Sencho streams `=== Deployment failed - restoring previous compose and env files ===`, restores the backed-up files, and re-runs `docker compose up -d` with the restored configuration. On success it streams `=== Restored previous compose and env files ===`. The restore reverts the compose and `.env` configuration. An image on a moving tag (such as `latest`) is not reverted, because the local tag still resolves to the newly pulled digest. The original deploy error is preserved as the deploy result, so a failed-then-rolled-back deploy still registers as a failure.
|
||||
|
||||
If the rollback itself fails (for example, the re-deploy step cannot pull a previously available image, or the file restore is blocked by filesystem permissions), Sencho streams `=== Rollback failed - manual intervention may be required ===`. The backup files remain at `<DATA_DIR>/backups/<nodeId>/<stack>/` so you can copy them back manually.
|
||||
If the rollback itself fails (for example, the re-deploy step cannot pull a previously available image, or the file restore is blocked by filesystem permissions), Sencho streams `=== Rollback failed. Manual intervention may be required ===`. The backup files remain at `<DATA_DIR>/backups/<nodeId>/<stack>/` so you can copy them back manually.
|
||||
|
||||
## Which operations are protected
|
||||
|
||||
@@ -29,39 +29,47 @@ A scheduled image-update task uses the same atomic wrapper as a manual update, s
|
||||
|
||||
## Manual rollback
|
||||
|
||||
The stack editor's action bar carries a **More actions** overflow menu (the three-dot icon next to **Update**). Open it and you'll see **Rollback** at the top, with the timestamp of the most recent backup rendered beneath the label. Selecting it restores the backed-up files and re-runs `docker compose up -d` non-atomically, to avoid nesting a rollback inside another atomic wrapper and overwriting the good backup with the broken state from the just-failed deploy.
|
||||
The stack editor's action bar has a **More actions** overflow menu (the three-dot icon next to **Update**). **Rollback** sits at the top, with the most recent backup's timestamp beneath the label. Selecting it restores the backed-up files and re-runs `docker compose up -d` non-atomically, so the rollback does not nest inside another atomic wrapper and overwrite the good backup with the just-failed state.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/atomic-deployments/rollback-menu.png" alt="Stack editor action bar with the More actions overflow menu open, showing the Rollback entry at the top with the backup timestamp rendered beneath the label, followed by Scan config and Delete entries" />
|
||||
<img src="/images/atomic-deployments/rollback-menu.png" alt="Stack editor header for the plex stack, with the More actions overflow menu open. The menu shows three entries: Rollback at the top with the backup timestamp beneath the label, then Scan config, then Delete." />
|
||||
</Frame>
|
||||
|
||||
The menu entry is hidden when no backup exists for the stack, for example on a freshly created stack that has never been deployed. The endpoint additionally requires the `stack:deploy` permission, so a user without it will see the menu entry but receive a permission error if they invoke it.
|
||||
The menu entry is hidden when no backup exists for the stack, for example on a freshly created stack that has never been deployed. It is also hidden for users who lack the `stack:deploy` permission; the backend enforces that check as the authoritative guard.
|
||||
|
||||
After a failed deploy or update, the stack page also surfaces a **Roll back** button in the recovery panel alongside Retry, Restart, and Refresh. This is the same rollback action, triggered in response to a failure rather than invoked on demand. See [Deploy Progress](/features/deploy-progress#recovery-actions) for the full recovery actions reference.
|
||||
|
||||
## Where backups are stored
|
||||
|
||||
Backups live under `<DATA_DIR>/backups/<nodeId>/<stack>/`, in the same writable volume Sencho uses for its database and other persisted state. They are intentionally kept outside the user's compose folder, so the operation works even when a container has chowned its bind-mounted stack directory to root.
|
||||
|
||||
Each backup is a flat copy of the compose file Sencho found, plus `.env` if it exists, plus two markers: a `.timestamp` recording when the backup was taken and a `.checksums` integrity manifest holding a SHA-256 for each backed-up file. There is one backup slot per stack: every protected deploy or update overwrites the previous backup, so the **Rollback** menu always reverts to the configuration that was on disk immediately before the most recent run.
|
||||
Each backup is a flat copy of the compose file Sencho found, plus `.env` and any configured project env files if they exist, plus two markers: a `.timestamp` recording when the backup was taken and a `.checksums` integrity manifest holding a SHA-256 for each backed-up file. There is one backup slot per stack: every protected deploy or update overwrites the previous backup, so the **Rollback** menu always reverts to the configuration that was on disk immediately before the most recent run.
|
||||
|
||||
A restore is a faithful revert, not an overlay. Sencho replaces the compose file and `.env` with the backed-up copies and removes any compose variant or `.env` that was added after the backup was taken, so the stack returns to exactly the file set it had before the run. For example, if a deploy switched the stack from `compose.yaml` to `docker-compose.yml` or introduced a new `.env`, a rollback undoes both. Files Sencho does not manage are left untouched.
|
||||
|
||||
Before a restore overwrites anything, Sencho re-hashes each backed-up file and compares it against the `.checksums` manifest. If a file no longer matches (for example, a backup truncated by an out-of-disk write), Sencho aborts the restore with a clear error and leaves the stack exactly as it was, rather than copying the corrupt content back over a working configuration.
|
||||
|
||||
## Rollback readiness
|
||||
|
||||
The **Stack Dossier** includes a **Rollback readiness** panel: a pre-flight read on whether rolling back will actually fix the problem. It evaluates several signals: whether a previous compose file exists and how old it is, whether a previous `.env` was captured, whether image tags are pinned or moving (moving tags are not reverted), the age of the last successful deploy, and whether healthchecks are defined to verify recovery. It also notes that application data (database rows, uploaded files, anything in a named volume) is outside the scope of any revert.
|
||||
|
||||
Check this panel in the dossier before rolling back a stack that has been running for a while. A rollback reverts only the compose and env files; if the problem is in a volume or in a database migration that already ran, rolling back the compose file alone will not help. See [Stack Dossier](/features/stack-dossier) for the full readout.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The Rollback option is not in the More actions menu">
|
||||
Sencho hides the entry whenever a rollback is not possible. The most common reason is that the stack has never been deployed, so no backup file exists yet. Run **Deploy** or **Update** once and the entry will appear.
|
||||
|
||||
A user without the `stack:deploy` permission will still see the menu entry; the rejection comes from the backend with a permission error after they click. Ask an admin to grant `stack:deploy` through **Settings · Roles & Access** if that happens.
|
||||
The entry is also hidden for users who lack the `stack:deploy` permission. Ask an admin to grant `stack:deploy` through **Settings · Access** if the entry does not appear.
|
||||
</Accordion>
|
||||
<Accordion title="The deploy succeeded but a service crashed seconds later">
|
||||
The health probe is a 3-second window after `docker compose up -d` returns. Crashes that happen after that window are out of scope for atomic rollback by design, because Sencho cannot tell whether a late exit is a real failure or a normal restart. The [health gate](/features/health-gated-updates) covers exactly this period: it observes the stack for a configurable window after the update, records a verdict on the stack timeline, and offers a manual rollback when containers do not stay healthy. For ongoing health beyond that, use **Auto-Heal Policies** to restart unhealthy containers automatically and **Alert Rules** to page you when a container exits unexpectedly.
|
||||
The health probe is a 3-second window after `docker compose up -d` returns. Crashes after that window are out of scope for atomic rollback, because Sencho cannot tell a late exit apart from a normal restart. The [health gate](/features/health-gated-updates) covers exactly this period: it observes the stack for a configurable window after the update, records a verdict on the stack timeline, and offers a manual rollback when containers do not stay healthy. For ongoing health beyond that, use **Auto-Heal Policies** to restart unhealthy containers automatically and **Alert Rules** to page you when a container exits unexpectedly.
|
||||
</Accordion>
|
||||
<Accordion title="The deploy progress modal showed 'Rollback failed - manual intervention may be required'">
|
||||
<Accordion title="The deploy progress modal showed 'Rollback failed. Manual intervention may be required'">
|
||||
This message means the auto-rollback attempted to restore the backup and re-deploy, but the restore step or the re-deploy itself errored out. The backup files are still at `<DATA_DIR>/backups/<nodeId>/<stack>/`. To recover:
|
||||
|
||||
1. Copy `compose.yaml` (or the variant Sencho backed up) and `.env` from `<DATA_DIR>/backups/<nodeId>/<stack>/` back into the stack directory.
|
||||
1. Copy `compose.yaml` (or the variant Sencho backed up), `.env`, and any project env files from `<DATA_DIR>/backups/<nodeId>/<stack>/` back into the stack directory.
|
||||
2. Open the stack in the editor and click **Deploy** to re-run with the restored configuration.
|
||||
|
||||
The most common causes are filesystem permissions on the stack directory and a missing image in a private registry that the original deploy could not pull.
|
||||
|
||||
@@ -3,15 +3,40 @@ title: Compose Doctor
|
||||
description: Run a preflight check on a stack before you deploy. Compose Doctor renders the effective Compose model and reports common failure modes, grouped by severity, with a clear fix for each.
|
||||
---
|
||||
|
||||
The **Doctor** tab in the right-hand **Anatomy** panel answers one question before you apply a change: *what will Docker actually run, and is it safe on this node?* Compose Doctor renders the effective Compose model, the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied, and then runs a set of deterministic checks against it and the node it would deploy to.
|
||||
The **Doctor** tab in the right-hand **Anatomy** panel answers one question before you apply a change: *what will Docker actually run, and is it safe on this node?* Compose Doctor renders the effective Compose model (the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied) and then runs a set of deterministic checks against it and the live Docker state on the node it would deploy to.
|
||||
|
||||
The check is advisory. It never blocks a deploy and never changes a stack; it tells you what it found so you can decide. It runs on demand: press **run preflight** and Sencho renders the model, runs the checks, and stores the result so the tab still shows it the next time you open the stack.
|
||||
The check is advisory. It never blocks a deploy and never changes a stack. It runs on demand: press **run preflight** and Sencho renders the model, runs all 30 checks, and stores the result so the tab still shows it the next time you open the stack.
|
||||
|
||||
Compose Doctor reads the structure of the effective model, service names, images, ports, volumes, and the *names* of environment variables, never their values, so a secret injected through `environment:` or `env_file:` never appears in the report, the stored run, or the logs. One caveat: a secret interpolated into a structural field, such as a port, an image tag, or a volume path built from a `${VAR}`, is resolved before the model is read, so its value does show. Keep secrets in `environment:` or `env_file:` rather than interpolating them into these fields. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
## Where to find it
|
||||
|
||||
## Severity
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-doctor/compose-doctor-tab.png"
|
||||
alt="The tab bar at the top of the Anatomy panel showing the Doctor tab selected"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Every finding is graded so you can triage at a glance. The tab groups findings under four headings and the summary line reflects the most severe one:
|
||||
The **Doctor** tab appears in the Anatomy panel header when the active node supports Compose Doctor. Click any stack in the sidebar, then switch to the **Doctor** tab.
|
||||
|
||||
## How it works
|
||||
|
||||
Every preflight run follows three steps:
|
||||
|
||||
1. **Render** the effective model. Sencho calls `docker compose config` on the stack, which resolves all variable interpolation, `include` directives, profile overrides, and `env_file` references into a single, normalized model.
|
||||
2. **Snapshot** live Docker state. Sencho reads which host ports are in use, which containers are running, and which named networks and volumes exist on the target node.
|
||||
3. **Run 30 deterministic rules** against the combination. Each rule is pure and produces zero or more findings with a severity, a message, and a suggested fix.
|
||||
|
||||
Sencho stores exactly one run per stack per node, so a new run immediately overwrites the previous one; there is no history.
|
||||
|
||||
## Privacy and environment values
|
||||
|
||||
Compose Doctor reads the structure of the effective model (service names, images, ports, volumes, and the *names* of environment variables), never their values. A secret injected through `environment:` or `env_file:` never appears in the report, the stored run, or the logs.
|
||||
|
||||
One caveat: a secret interpolated into a structural field (such as a port, an image tag, or a volume path built from `${VAR}`) is resolved before the model is read, so its value does show in the rendered model. Render errors are redacted before storage; Sencho never writes the raw `docker compose config` error to the database. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
|
||||
## Severity levels
|
||||
|
||||
Every finding is graded so you can triage at a glance. The tab groups findings under four headings and the summary reflects the most severe one:
|
||||
|
||||
| Severity | Meaning |
|
||||
|----------|---------|
|
||||
@@ -20,50 +45,235 @@ Every finding is graded so you can triage at a glance. The tab groups findings u
|
||||
| **Warning** | A reliability or reproducibility concern, such as a moving `latest` tag or a missing restart policy. |
|
||||
| **Info** | Something to be aware of, such as a network or volume that will be created on first deploy. |
|
||||
|
||||
A stack with unresolved blocker or high-risk findings also shows a small coloured dot on the **Doctor** tab, so you can see there is something to look at without opening it.
|
||||
## Run status
|
||||
|
||||
The summary card at the top of the Doctor tab reflects the overall outcome of the last run:
|
||||
|
||||
| Status | When it appears | Summary card |
|
||||
|--------|-----------------|--------------|
|
||||
| **No run yet** | Preflight has never been triggered for this stack on this node | Placeholder with "no preflight yet" and a Stethoscope icon |
|
||||
| **All clear** | Model rendered; no findings | Green card with "No issues found in the effective model." |
|
||||
| **Cannot render** | `docker compose config` failed | Red card with the (redacted) render error message |
|
||||
| **Blocker** | Highest finding is a blocker | Red card with a count of findings by severity |
|
||||
| **High risk** | Highest finding is high risk | Amber card with a count of findings by severity |
|
||||
| **Warning** | Highest finding is a warning | Blue card with a count of findings by severity |
|
||||
| **Info** | All findings are informational | Muted card with a count of findings by severity |
|
||||
|
||||
The summary card also shows when the run happened and who triggered it: "ran 5 minutes ago by admin".
|
||||
|
||||
A small colored dot appears on the **Doctor** tab label when the last run found blockers (red) or high-risk issues (amber). Opening the tab removes neither the dot nor the findings; run preflight again after fixing the issues to clear them.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-doctor/compose-doctor-severity-dot.png"
|
||||
alt="The tab bar showing a small amber dot on the Doctor tab label indicating a high-risk finding from the last preflight run"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## What it checks
|
||||
|
||||
Compose Doctor renders the model first. If `docker compose config` cannot produce a model, the report says so and stops, since nothing else can be trusted. When the model renders, the checks cover:
|
||||
All 30 rules are listed below, organized by topic.
|
||||
|
||||
- **Environment.** Variables referenced by the model but not set in any consulted env file, which Compose silently turns into empty strings.
|
||||
- **Ports.** Host ports already in use by another stack on the node, two services in the same stack claiming the same port, and ports published on all interfaces (`0.0.0.0`).
|
||||
- **Host paths.** Bind-mount directories that do not exist yet, and paths that look likely to have ownership problems for a container running as a non-root user.
|
||||
- **Privilege and exposure.** A mounted Docker socket, `privileged: true`, and `network_mode: host`.
|
||||
- **Reliability.** Images pinned to a moving `latest` tag, services with no restart policy, and services with no healthcheck.
|
||||
- **Compose semantics.** `deploy` fields that standalone Compose ignores, and required external networks or volumes that do not exist on the node.
|
||||
- **Surprises.** Duplicate or already-taken `container_name`s, and services that the effective model pulls in via `include` or `extends` that are not in the file you are looking at.
|
||||
### Model rendering
|
||||
|
||||
Each finding states what was detected, why it matters, where in the model it came from, and how to fix it.
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Compose model could not be rendered | Blocker | `docker compose config` failed; the model cannot be assessed and all other rules are skipped. Fix the reported error first. |
|
||||
|
||||
### Environment
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Unset variable | High | A variable referenced by the model has no value in the environment or any consulted env file. Compose silently substitutes an empty string, which often breaks the container without a clear error. |
|
||||
| Missing env file | High | A path listed under `env_file:` does not exist in the stack directory. Compose fails to start the stack when a required env file is absent. |
|
||||
|
||||
### Port conflicts
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Host port is already in use | Blocker | Another stack or unmanaged container on this node already binds the same host port. The deploy will fail. Requires live Docker state; skipped when the daemon is unreachable. |
|
||||
| Two services publish the same port | Blocker | Two services within this stack both claim the same host port on overlapping interfaces. Only one can bind it. |
|
||||
| Port exposed on all interfaces | High | A port is published on all interfaces (`0.0.0.0`), making it reachable from every network the host is attached to. |
|
||||
|
||||
### Bind mounts
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Bind mount path is missing | High | A relative host path referenced by the model does not exist inside the node's Compose base directory. Docker will create it as a root-owned directory on deploy, which often leaves the container unable to write to it. |
|
||||
| Bind mount may have wrong ownership | Warning | A resolvable path is owned by root but the container runs as a non-root user. Applies to POSIX nodes only. |
|
||||
|
||||
### Security
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Docker socket mounted | High | A service mounts `docker.sock`, which grants it root-equivalent control over the host. |
|
||||
| Privileged container | High | A service runs with `privileged: true`, disabling most container isolation. |
|
||||
| Host network mode | High | A service uses `network_mode: host`, bypassing Docker's network isolation and ignoring published-port mappings. |
|
||||
| Check UID/GID alignment | Warning | A service sets a UID or GID and mounts host paths that Sencho cannot inspect from inside its container. Mismatched ownership between the host path and the container user is a common source of permission errors. |
|
||||
|
||||
### Reliability
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Image uses a moving tag | Warning | A service uses `:latest` or a tag-less image reference, making deploys non-reproducible and subject to unexpected changes. |
|
||||
| No restart policy | Warning | A service has no restart policy and will not come back after a crash or host reboot. |
|
||||
| No healthcheck | Warning | A service declares no healthcheck in the Compose model. The image itself may define one, but Sencho cannot see it from the model alone. |
|
||||
|
||||
### Compose semantics
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Swarm-only deploy fields | Warning | A service sets `deploy` keys (`placement`, `update_config`, `rollback_config`, `endpoint_mode`) that standalone Compose ignores; these apply to Swarm only. |
|
||||
| Duplicate container_name | Blocker | Two services within this stack both set the same `container_name`. Docker requires unique names; the deploy will fail. |
|
||||
| Anonymous volume in use | Info | A service mounts one or more anonymous volumes. Anonymous volumes have no name, so they are easy to miss when backing up and are orphaned when the container is recreated. |
|
||||
| Effective model adds services | Info | The effective model includes services not visible in this file, pulled in via `include`, `extends`, or profiles. What deploys may differ from what you see here. |
|
||||
|
||||
### Node state
|
||||
|
||||
The rules in this category (except "Node-state checks skipped" itself) read live Docker state. When the daemon is unreachable, they are all skipped and the "Node-state checks skipped" info finding is added in their place. "Host port is already in use" in Port conflicts also reads node state and is skipped under the same conditions. See [Node-state checks and graceful degradation](#node-state-checks-and-graceful-degradation).
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Node-state checks skipped | Info | The Docker daemon could not be reached; node-state rules did not run and this result is partial. |
|
||||
| External network not found | Blocker | A network declared `external: true` does not exist on this node. The deploy will fail. |
|
||||
| External volume not found | Blocker | A volume declared `external: true` does not exist on this node. The deploy will fail. |
|
||||
| New network will be created | Info | A non-external network that does not yet exist on this node will be created on first deploy. |
|
||||
| New volume will be created | Info | A named volume that does not yet exist on this node will be created on first deploy. |
|
||||
| container_name already in use | Blocker | A service's `container_name` is already used by a different stack or unmanaged container on this node. The deploy will fail with a name conflict. |
|
||||
|
||||
### Exposure intent
|
||||
|
||||
These rules activate when the stack publishes at least one host port. They use the exposure intent you set in the Networking tab and any access URLs documented in the Stack Dossier to flag mismatches. See [Exposure intent checks](#exposure-intent-checks).
|
||||
|
||||
| Rule | Severity | What it detects |
|
||||
|------|----------|----------------|
|
||||
| Classified internal but publishes a host port | High | A service is classified as internal or same-node exposure but publishes a host port that contradicts that intent. |
|
||||
| Sensitive service exposed on all interfaces | High | A service that looks like a database or admin tool (postgres, redis, adminer, and similar) publishes on all interfaces. |
|
||||
| Stack publishes ports without an exposure intent | Warning | The stack publishes host ports but no exposure intent has been set in the Networking tab. |
|
||||
| Published port not in documented access URLs | Warning | A host port is not referenced by any access URL recorded in the Stack Dossier; the documentation may be stale. |
|
||||
| Has reverse-proxy labels but no documented URL | Warning | A service carries Traefik, Caddy, or similar reverse-proxy labels but has no documented access URL or reverse-proxy intent set. |
|
||||
|
||||
## Exposure intent checks
|
||||
|
||||
Five of the 30 rules cross-reference the stack's exposure intent and the access URLs documented in the Stack Dossier. These rules only fire when the stack publishes at least one host port.
|
||||
|
||||
To resolve exposure-related findings:
|
||||
|
||||
- **Set an exposure intent** in the [Compose Networking](/features/compose-networking) tab. Options are: internal (no host access), same-node (loopback only), LAN, reverse proxy (traffic arrives via a proxy), and public.
|
||||
- **Document access URLs** in the [Stack Dossier](/features/stack-dossier). When a dossier URL references a host port, the port-vs-dossier rule uses that to confirm the port is intentional and expected.
|
||||
|
||||
Once an intent and access URLs are set, Sencho can detect when the configuration contradicts the documented intent, making future runs more precise.
|
||||
|
||||
## Node-state checks and graceful degradation
|
||||
|
||||
Six of the 30 rules require live Docker state to run: five are in the Node state category (external networks and volumes, new-resource notices, and container_name collision) and one is "Host port is already in use" in Port conflicts. All six are skipped when the Docker daemon is unreachable.
|
||||
|
||||
When the daemon is unreachable:
|
||||
|
||||
- All model-only rules still run and produce findings as normal.
|
||||
- All node-state rules are skipped.
|
||||
- An info finding ("Node-state checks skipped") is added to make the gap visible.
|
||||
- The run completes with a valid status reflecting whatever model-only rules found.
|
||||
|
||||
A partial run (where node-state checks were skipped) is stored and displayed like any other run. Run preflight again once the node is reachable to get full coverage.
|
||||
|
||||
## Health-Gated Updates
|
||||
|
||||
The last stored preflight result feeds directly into the [Health-Gated Updates](/features/health-gated-updates) readiness check. When you trigger **Update** from a stack, Sencho opens a readiness dialog before pulling anything. The dialog reads your most recent preflight result:
|
||||
|
||||
- A blocker finding sets the update verdict to **Blocked**.
|
||||
- One or more high-risk findings set the verdict to **Review required**.
|
||||
- Preflight that has never run is noted in the dialog without dragging the verdict down.
|
||||
|
||||
Running preflight before updating gives the readiness check the most accurate signal. A clean pass after resolving findings contributes a positive readiness signal for that stack.
|
||||
|
||||
## Running preflight
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-doctor/compose-doctor-never-run.png"
|
||||
alt="The Doctor tab in the never-run state, showing the 'no preflight yet' placeholder with a Stethoscope icon and the run preflight button in the top-right corner"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
2. Switch to the **Doctor** tab in the Anatomy panel header.
|
||||
3. Press **run preflight**. The summary updates with the result and findings grouped by severity.
|
||||
4. Fix anything that matters, then run it again to confirm it clears.
|
||||
3. Press **run preflight**. Sencho renders the effective model, runs all checks, and updates the tab with the result.
|
||||
4. Fix anything that matters, then run it again to confirm the findings clear.
|
||||
|
||||
Preflight runs against the **active node**, so selecting a remote node checks the stack on the machine that actually owns it. On a phone, the same report appears under the **Compose** section of the stack detail.
|
||||
Preflight runs against the **active node**, so selecting a remote node checks the stack against that machine's live Docker state. On mobile, the same report appears under the **Compose** section of the stack detail view.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-doctor/compose-doctor-findings.png"
|
||||
alt="The Doctor tab showing an amber high-risk summary card with counts for high-risk and warning findings, followed by grouped finding rows each showing a service tag, title, explanation, suggested fix, and source file path"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Capability gating
|
||||
|
||||
The **Doctor** tab only appears when the active node reports the `compose-doctor` capability. A node running an older version of Sencho does not advertise this capability and the tab is hidden for that node until it is updated.
|
||||
|
||||
There is no tier gate: Compose Doctor is available on all plans.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Advisory only.** Compose Doctor never blocks a deploy or changes any stack configuration. Act on findings or ignore them; the choice is always yours.
|
||||
- **Bind-mount checks are scoped.** Only paths that resolve inside the node's Compose base directory can be checked for existence and ownership. Absolute host paths outside that directory (such as `/mnt/media`) are not reported as missing.
|
||||
- **Healthcheck rule cannot see image-level healthchecks.** The no-healthcheck rule fires when the Compose model does not declare a healthcheck. Many images define one internally that Sencho cannot see from the rendered model; treat the finding as a prompt to confirm the image provides one.
|
||||
- **One run stored per node.** There is no history. Each new run overwrites the previous one for that stack on that node.
|
||||
- **Node-state rules require a reachable Docker daemon.** See [Node-state checks and graceful degradation](#node-state-checks-and-graceful-degradation).
|
||||
- **Port conflict check excludes the checked stack.** Host ports already held by the stack being checked are ignored, so redeploying a running stack does not generate a false conflict with itself.
|
||||
- **Exposure intent rules require published ports.** All five exposure intent rules are skipped entirely when the stack publishes no host ports.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The report says it cannot render the model">
|
||||
Compose Doctor runs `docker compose config` to resolve the effective model, and that command failed. The usual causes are a YAML syntax error, an unresolved `include` or `merge`, or a required variable with no value. Fix the Compose or env file and run preflight again. Sencho deliberately does not echo the raw error, since it can contain values from your files.
|
||||
Compose Doctor runs `docker compose config` to resolve the effective model, and that command failed. Common causes are a YAML syntax error, an unresolved `include` or `merge` key, or a required variable with no value. Fix the Compose or env file and run preflight again. Sencho does not echo the raw error in the UI because it can contain values from your files.
|
||||
</Accordion>
|
||||
<Accordion title="A bind mount I use every day is flagged as missing">
|
||||
Compose Doctor can only check paths that live inside the node's Compose directory, such as a relative `./data` mount. An absolute host path like `/mnt/media` is outside what Sencho can see from inside its container, so it is never reported as missing. A missing relative path is real: Docker would create it as a root-owned directory on deploy.
|
||||
Compose Doctor can only check paths that resolve inside the node's Compose base directory, such as a relative `./data` mount. An absolute host path like `/mnt/media` is outside what Sencho can see from inside its container and is never reported as missing. A missing relative path is a real finding: Docker would create it as a root-owned directory on deploy.
|
||||
</Accordion>
|
||||
<Accordion title="Every service is flagged for no healthcheck">
|
||||
Compose Doctor reports a healthcheck only when the Compose file does not declare one. Many images define their own healthcheck internally, which Sencho cannot see from the model alone, so treat these as a prompt to confirm rather than a hard problem.
|
||||
The no-healthcheck rule fires when the Compose model does not declare a healthcheck. Many images define their own healthcheck internally, which Sencho cannot see from the model alone. Treat these findings as a prompt to confirm the image provides a healthcheck rather than a hard problem.
|
||||
</Accordion>
|
||||
<Accordion title="A port conflict is flagged for a port my own stack uses">
|
||||
Preflight ignores ports already held by the stack you are checking, so redeploying a running stack does not flag its own bindings. A conflict means a *different* stack, or an unmanaged container, holds that host port on this node.
|
||||
Preflight ignores ports already held by the stack being checked, so redeploying a running stack does not flag its own bindings. A conflict finding means a different stack or an unmanaged container holds that host port on this node.
|
||||
</Accordion>
|
||||
<Accordion title="The report says node-state checks were skipped">
|
||||
The node-state checks (external networks and volumes, host-port conflicts, and `container_name` collisions) read the node's live Docker state. When the Docker daemon on the target node is unreachable, Sencho cannot read that state, so those checks are skipped and the report shows an info notice rather than guessing. The model checks still run. Confirm the daemon is reachable on the node, then run preflight again for full coverage.
|
||||
The node-state checks (external networks and volumes, host-port conflicts, and container_name collisions) read live Docker state. When the Docker daemon on the target node is unreachable, those checks are skipped and the report shows an info finding instead of those results. The model checks still run. Confirm the daemon is reachable on the node, then run preflight again for full coverage.
|
||||
</Accordion>
|
||||
<Accordion title="The Doctor tab is not there on a remote node">
|
||||
The tab appears when the active node reports that it supports Compose Doctor. A node running an older version of Sencho does not advertise it, so the tab is hidden for that node until it is updated.
|
||||
<Accordion title="The Doctor tab is not visible on a remote node">
|
||||
The tab appears when the active node reports that it supports Compose Doctor. A node running an older version of Sencho does not advertise that capability, so the tab is hidden for that node until it is updated.
|
||||
</Accordion>
|
||||
<Accordion title="Exposure intent rules are firing unexpectedly">
|
||||
The five exposure intent rules activate when the stack publishes at least one host port. The "unclassified" warning clears as soon as you set an exposure intent in the Networking tab. The "port not in dossier" warning clears once you add a matching access URL in the Stack Dossier. If an intent is already set and the finding still fires, check that the intent is configured on the correct node and for the correct service.
|
||||
</Accordion>
|
||||
<Accordion title="I want to see who ran preflight last">
|
||||
The summary card shows the username and relative time: "ran 5 minutes ago by admin". This reflects the most recent run stored for this stack on the currently active node.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Compose Networking" icon="network-wired" href="/features/compose-networking">
|
||||
Set the exposure intent that drives the exposure intent preflight rules.
|
||||
</Card>
|
||||
<Card title="Stack Dossier" icon="book-open" href="/features/stack-dossier">
|
||||
Document access URLs that Compose Doctor uses to verify published ports are intentional.
|
||||
</Card>
|
||||
<Card title="Health-Gated Updates" icon="shield-alt" href="/features/health-gated-updates">
|
||||
Preflight results feed directly into the update readiness check before every update.
|
||||
</Card>
|
||||
<Card title="Environment Guardrails" icon="key" href="/features/environment-guardrails">
|
||||
Keep secrets out of structural Compose fields to avoid them appearing in the rendered model.
|
||||
</Card>
|
||||
<Card title="Stack Drift" icon="code-branch" href="/features/stack-drift">
|
||||
Drift detection checks whether what is running still matches the Compose file.
|
||||
</Card>
|
||||
<Card title="Compose Storage" icon="database" href="/features/compose-storage">
|
||||
Understand named and anonymous volumes before running preflight on storage-heavy stacks.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,78 +1,249 @@
|
||||
---
|
||||
title: Compose Networking
|
||||
description: Inspect how a stack is networked and exposed, classify what its exposure should be, and see where the running containers disagree with the Compose file, all without opening a terminal.
|
||||
description: Inspect how a stack is connected and exposed, classify its intended exposure, and see where the running containers disagree with the Compose file, all without opening a terminal.
|
||||
---
|
||||
|
||||
The **Networking** tab in the right-hand **Anatomy** panel answers a Compose-first question: *is this stack networked and exposed the way the Compose file says it is, and are the dangerous or confusing parts visible before I deploy?* It renders the effective Compose model, the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied, pairs it with the live Docker state when that node is reachable, and shows the result as plain facts.
|
||||
The **Networking** tab in the right-hand **Anatomy** panel answers two questions about any stack: *how is it networked and exposed according to the Compose file*, and *does what is actually running agree with that?* It renders the effective Compose model (the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied), pairs it with a live Docker snapshot when the node is reachable, and presents both as plain, scannable facts.
|
||||
|
||||
The view is read-only with respect to the stack: it never changes a deployment. The one thing you can edit here is the stack's *exposure intent*, which is stored separately so Sencho can flag mismatches over time.
|
||||
The tab is read-only with respect to stack deployments: it never alters a container, a Compose file, or an existing network attachment. Two write operations are available: setting the stack's *exposure intent* (stored separately so Sencho can flag mismatches over time) and creating a new Docker network on the active node, which is available to admins and covered in [Creating networks](#creating-networks) below.
|
||||
|
||||
Compose Networking reads the structure of the model, network names, service-to-network membership, published ports, and the *names* of environment variables and labels, never their values, so a secret injected through `environment:` or `env_file:` never appears in the view or the logs. One caveat: a structural field built by interpolating a secret, such as a network name, a published port, or an `extra_hosts` entry assembled from a `${VAR}`, is resolved before the tab reads it, so its value does show. Keep secrets in `environment:` or `env_file:` rather than interpolating them into these fields. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
Compose Networking reads structure: network names, service-to-network membership, published ports, network modes, and the *names* of environment variables and labels, never their values. A secret injected through `environment:` or `env_file:` never appears in the view or the logs. One caveat: a structural field assembled by interpolating a secret (a network `name:`, a published port, or an `extra_hosts` entry built from `${VAR}`) is resolved before the inspector reads it, so its value does show. Keep secrets in `environment:` or `env_file:` rather than interpolating them into structural fields. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
|
||||
## Networks
|
||||
<img
|
||||
src="/images/compose-networking/networking-tab-overview.png"
|
||||
alt="The Networking tab showing the full panel: EXPOSURE INTENT section with REVERSE-PROXY selected for the stack and INHERIT for the service, NETWORKS section listing arr-net with an external badge, SERVICES section showing swag with ports 443/tcp and 80/tcp both marked 'all interfaces', and RUNTIME DRIFT section showing the green 'runtime matches compose' card"
|
||||
/>
|
||||
|
||||
The top of the tab lists the stack's networks with the facts that matter:
|
||||
## Accessing the Networking tab
|
||||
|
||||
- The resolved Docker network name, and the Compose key it came from.
|
||||
- **external** when the network is one the stack expects to already exist on the node.
|
||||
- **internal** when the network has no outbound or host connectivity.
|
||||
- **created by stack** when deploying the stack will create the network.
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
2. Switch to the **Networking** tab in the Anatomy panel header.
|
||||
|
||||
## Published ports and bindings
|
||||
On a phone, the same information appears under the **Compose** section of the stack detail.
|
||||
|
||||
Each service lists its published ports with the interface they bind to:
|
||||
The tab appears only when the active node reports that it supports Compose Networking. A node running an older version of Sencho hides the tab until it is updated.
|
||||
|
||||
- **all interfaces** marks a port published on `0.0.0.0`, reachable from every network the host is attached to.
|
||||
- **loopback** marks a port bound only to `127.0.0.1`, reachable only from the host itself.
|
||||
- A specific address is shown as-is.
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-tab-location.png"
|
||||
alt="The Anatomy panel tab strip showing ANATOMY, ACTIVITY, DOSSIER, DRIFT, ENVIRONMENT, NETWORKING (active and underlined), DOCTOR, STORAGE tabs alongside FILES and EDIT action buttons"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
A service running with `network_mode: host` is treated as **host-exposed**: it publishes every container port directly on the host, so it counts as exposed regardless of whether it declares any `ports:`. The exposure summary reflects that even when the port list is empty.
|
||||
## Effective model rendering
|
||||
|
||||
The tab also surfaces each service's network membership and aliases, `network_mode` (`host`, `none`, `service:`, and `container:` modes are called out), and `extra_hosts` entries.
|
||||
When you open the tab, Sencho runs `docker compose config` on the active node to produce the effective model: the Compose file with all variable substitutions resolved, any `include:` and `extends:` directives merged, profiles filtered, and `.env` and `env_file` values applied. The networking facts you see always reflect this resolved model, not the raw file text.
|
||||
|
||||
**Network name resolution** follows Compose rules:
|
||||
|
||||
- By default, Docker names a network `<project>_<key>`, where `project` is the Compose project name (the `name:` field in the file, or the stack's directory name if absent) and `key` is the network's Compose key.
|
||||
- If the network block declares its own `name:` field, that exact name is used instead of the `<project>_<key>` default.
|
||||
- External networks use the Compose key directly (or the `name:` field if set), because they are expected to already exist on the host rather than being created by the stack.
|
||||
|
||||
When the Compose key and the resolved Docker name differ, the tab shows both: the Docker name in full text and the key in parentheses beside it.
|
||||
|
||||
If `docker compose config` cannot produce a model (usually a YAML error, an unresolved variable, or a broken `include`), the tab shows the error message and cannot display any facts. Fix the Compose file and reopen the tab to continue.
|
||||
|
||||
## Exposure intent
|
||||
|
||||
Exposure intent is how you tell Sencho what a stack *should* be reachable from, so it can warn you when the Compose file says otherwise. Set it for the whole stack, or override it per service:
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-exposure-intent.png"
|
||||
alt="The exposure intent section showing a stack row with REVERSE-PROXY highlighted, and one service row (swag) with INHERIT highlighted and an arrow indicator reading 'reverse-proxy', showing the intent inherited from the stack"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Exposure intent is how you tell Sencho what a stack *should* be reachable from, so it can warn you when the runtime says otherwise. Set one intent for the whole stack, or override it per service when individual services have different exposure profiles.
|
||||
|
||||
| Intent | Meaning |
|
||||
|--------|---------|
|
||||
| **internal** | Not published to the host at all. |
|
||||
| **same-node** | Reachable only from the host (loopback bindings). |
|
||||
| **LAN** | Published for the local network. |
|
||||
| **reverse proxy** | Reached through a reverse proxy, not a direct host port. |
|
||||
| **internal** | Not reachable from the host at all. Containers communicate only within Docker networks. |
|
||||
| **same-node** | Reachable only from the host itself, through loopback-bound ports. |
|
||||
| **lan** | Published for the local network. |
|
||||
| **reverse-proxy** | Reached through a reverse proxy; no direct host port exposure expected. |
|
||||
| **public** | Intentionally reachable from the internet. |
|
||||
| **temporary** | A short-lived exposure (a classification label only). |
|
||||
| **temporary** | A short-lived exposure (a label for tracking only, not a functional configuration). |
|
||||
| **unknown** | Not yet classified. |
|
||||
|
||||
A service with no intent of its own inherits the stack's. Clearing a service returns it to **inherit**; clearing the stack returns it to unclassified. Sencho stores the intent apart from the generated facts, so when the two drift apart the **Doctor** tab can point it out.
|
||||
**How inheritance works:** a service with no intent of its own inherits the stack's. In the per-service row, the **inherit** pill is selected when no override is set; an arrow indicator shows the intent the service inherits from the stack. Clearing a service's override returns it to **inherit**. Clearing the stack's intent returns it to unclassified.
|
||||
|
||||
## Findings
|
||||
**Permissions:** editing the intent requires stack edit access. With read-only access you can see the current classification but not change it.
|
||||
|
||||
Risk findings live in the **Doctor** tab, which reads the same model. On top of its existing deploy and security checks (host-port conflicts, missing external networks, host networking, broad exposure, and a mounted Docker socket), it adds exposure-aware findings:
|
||||
**How Doctor uses it:** Sencho stores the intent separately from the computed networking facts. When the two drift apart (a service classified `internal` that is publishing a host port, for example), the **Doctor** tab flags the mismatch. See [Compose Doctor](/features/compose-doctor) for the full list of exposure-aware findings.
|
||||
|
||||
- A service classified **internal** or **same-node** that publishes a host port.
|
||||
- A database or admin image published on every interface.
|
||||
- A stack that publishes ports but has no exposure intent set.
|
||||
- A published port that the Stack Dossier's documented access URLs do not mention.
|
||||
- Reverse-proxy labels with no documented URL or reverse-proxy intent.
|
||||
## Networks
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-networks.png"
|
||||
alt="The networks section showing one network row: 'arr-net' with a blue external badge, indicating the network is expected to already exist on the host"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
The Networks section lists every network the effective model declares:
|
||||
|
||||
| Element | What it shows |
|
||||
|---------|--------------|
|
||||
| **Name** | The resolved Docker network name Compose will use. |
|
||||
| **Key** | The Compose key in parentheses, when it differs from the resolved name. |
|
||||
| **external** badge | The network is expected to already exist on the host. The stack does not create or remove it on deploy. |
|
||||
| **internal** badge | The network has no outbound connectivity; containers on it cannot reach the internet or the host network. |
|
||||
| **created by stack** badge | Deploying the stack will create this network; it is absent until then. |
|
||||
|
||||
When a stack declares no explicit networks, the section shows **default network only**: all services share the implicit Docker bridge that Compose creates automatically.
|
||||
|
||||
External networks that do not exist on the node when you deploy will cause the deploy to fail. Compose Doctor flags missing external networks before you apply a change.
|
||||
|
||||
## Services
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-services.png"
|
||||
alt="The services section showing a card for the 'swag' service with network membership on 'arr-net', two published ports (443/tcp and 80/tcp), each with an amber 'all interfaces' badge indicating they are bound to 0.0.0.0"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Each service gets a card that summarises its networking configuration as Compose will actually apply it.
|
||||
|
||||
### Network membership and aliases
|
||||
|
||||
The card lists the networks each service belongs to. When a service declares `aliases:` for a network, those names appear in parentheses after the network key. Aliases are additional DNS names the service is reachable by within that network: any other service on the same network can connect to this one using the alias as a hostname, in addition to the default service name.
|
||||
|
||||
### Port bindings
|
||||
|
||||
Published ports appear with a badge indicating which interface they are bound to on the host:
|
||||
|
||||
| Badge | Colour | What it means |
|
||||
|-------|--------|---------------|
|
||||
| **all interfaces** | Amber | The port is bound to `0.0.0.0` (or `::` for IPv6), making it reachable from every network the host is attached to. |
|
||||
| **loopback** | Green | The port is bound to `127.0.0.1` (or `::1`), reachable only from the host itself. |
|
||||
| *Specific IP* | Neutral | The port is bound to the address shown. |
|
||||
|
||||
Port ranges appear as `startPort-endPort/protocol`, for example `8000-8002/tcp`.
|
||||
|
||||
### Network modes
|
||||
|
||||
When a service uses `network_mode:` instead of individual network attachments, the mode appears as an amber badge beside the service name:
|
||||
|
||||
- **`network_mode: host`**: the service shares the host network stack entirely. Every container port is accessible directly on the host, even without any `ports:` entries. The card shows **all container ports / host-exposed** to make this explicit.
|
||||
- **`network_mode: none`**: the service has no network connectivity at all.
|
||||
- **`network_mode: service:<name>`** or **`network_mode: container:<name>`**: the service shares another container's network namespace.
|
||||
|
||||
### Extra hosts
|
||||
|
||||
When a service declares `extra_hosts:`, the resolved hostname-to-IP mappings are listed at the bottom of the card. These entries are injected into the container's `/etc/hosts` file at startup.
|
||||
|
||||
## Runtime drift
|
||||
|
||||
When the node is reachable, the tab compares the running containers to the Compose file and reports where they disagree: a container on a network the file does not declare, a container on a network owned by another stack, and a declared network that no running service uses or that is missing from the runtime. These also appear on the **Drift** tab, where their history is tracked over time. When the node is not reachable, the tab shows the declared model only and says so.
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-runtime-matches.png"
|
||||
alt="The runtime drift section showing a green card with a globe icon and the text 'runtime matches compose' in uppercase"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
When the node is reachable, the tab compares the declared effective model against the live Docker state and reports where they disagree. Four types of drift are detected:
|
||||
|
||||
| Finding | What it means |
|
||||
|---------|---------------|
|
||||
| **Attached to undeclared network** | A running container is connected to a network the Compose file does not declare for that service. Common after a manual `docker network connect` or a Compose file change that has not been applied yet. |
|
||||
| **Foreign network attachment** | A container from this stack is connected to a network owned by a different stack. Cross-stack networking the file does not account for. |
|
||||
| **Declared but unused** | A network is declared in the Compose file but no currently running service is connected to it. Often seen when a service is stopped or removed without `docker compose down`. |
|
||||
| **Missing from runtime** | A network is declared but does not exist in Docker. The stack may not have been deployed, or the network was deleted externally. |
|
||||
|
||||
System-managed networks (`bridge`, `host`, `none`) and Docker's implicit default bridge are excluded from all drift findings.
|
||||
|
||||
When the runtime matches the Compose file, the section shows a green **runtime matches compose** card.
|
||||
|
||||
When Docker is not reachable, the section shows **runtime unavailable, showing the declared model only**. The networks, ports, and exposure facts are still accurate; drift comparison resumes once the node is reachable.
|
||||
|
||||
**Relationship to the Drift tab:** the runtime drift shown here uses the same comparison logic as the [Drift Detection](/features/stack-drift) tab. The difference is history: the Drift tab records findings over time and shows when drift first appeared and when it cleared. The Networking tab always shows the current live state.
|
||||
|
||||
## Doctor findings
|
||||
|
||||
When Compose Doctor is available on the active node, a prompt at the bottom of the Networking tab directs you to the Doctor tab for deploy and security checks. The Doctor tab reads the same effective model and, when exposure intents are set, adds five exposure-aware findings on top of its standard checks:
|
||||
|
||||
1. A service classified **internal** or **same-node** that publishes a host port.
|
||||
2. A database or admin image published on all interfaces.
|
||||
3. A stack that publishes ports but has no exposure intent set.
|
||||
4. A published port that the Stack Dossier's documented access URLs do not mention.
|
||||
5. Reverse-proxy labels with no documented URL or reverse-proxy intent set.
|
||||
|
||||
See [Compose Doctor](/features/compose-doctor) for the complete check set and severity levels.
|
||||
|
||||
## Creating networks
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-networking/networking-create-dialog.png"
|
||||
alt="The Create network dialog showing a Name field, a Driver dropdown set to bridge, optional Subnet and Gateway inputs, and Internal and Attachable toggles"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Admins can create a new Docker network directly from the Networking tab using the **create network** button in the panel header. This is the same action available from the Resources Hub Networks tab.
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| **Name** | Yes | The Docker network name. Must be unique on the node. |
|
||||
| **Driver** | Yes (default: bridge) | `bridge` for isolated single-host networks; `overlay` for multi-host Swarm networks; `macvlan` to assign MAC addresses; `host` or `none` for special cases. |
|
||||
| **Subnet** | No | CIDR notation, e.g. `172.20.0.0/16`. Docker chooses a subnet automatically when left blank. |
|
||||
| **Gateway** | No | Gateway IP for the subnet, e.g. `172.20.0.1`. |
|
||||
| **Internal** | No | Prevents outbound connectivity from containers on this network. |
|
||||
| **Attachable** | No | Allows containers outside Compose to connect to the network manually. |
|
||||
|
||||
After the network is created, the Networking tab refreshes automatically. The network appears in the Networks section of any stack that declares it as `external:`.
|
||||
|
||||
<Note>
|
||||
Creating a network here does not attach it to the current stack. To use the new network in a stack, add it to the Compose file under `networks:` with `external: true` (since Sencho created it, not the stack) and redeploy.
|
||||
</Note>
|
||||
|
||||
## Limitations
|
||||
|
||||
- **No modify or delete.** Sencho can create networks (via the create network dialog) but never modifies or deletes existing networks from this tab. Cleanup is handled by `docker compose down` or the Resources Hub prune actions.
|
||||
- **Structural fields only.** Environment variable values and label values are never returned. Secrets interpolated into structural fields (network `name:`, ports, `extra_hosts`) are resolved and do appear.
|
||||
- **Runtime drift requires Docker reachability.** When the node is not reachable, the tab shows the declared model but cannot compare against the live state.
|
||||
- **Capability-gated tab.** The Networking tab only appears when the active node reports that it supports Compose Networking. Older nodes hide the tab until they are updated.
|
||||
- **Render failure blocks all facts.** If `docker compose config` fails, the tab cannot show any networking information until the model can be rendered. The error message is shown, but raw Docker output is redacted.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The Networking tab is not there">
|
||||
The tab appears once the node advertises support for it. A node running an older Sencho version hides the tab until it is updated.
|
||||
The tab appears once the active node advertises support for it. A node running an older version of Sencho hides the tab until it is updated.
|
||||
</Accordion>
|
||||
<Accordion title="The view says the runtime is unavailable">
|
||||
Sencho could not reach Docker on that node, so it shows the declared Compose model only and skips runtime drift. The networks, ports, and exposure facts are still accurate; the live comparison resumes once the node is reachable.
|
||||
</Accordion>
|
||||
<Accordion title="It says the model cannot render">
|
||||
`docker compose config` could not produce an effective model, usually a YAML error, an unresolved include or merge, or a required variable with no value. Fix the reported problem and reopen the tab.
|
||||
`docker compose config` could not produce an effective model, usually a YAML error, an unresolved include or merge, or a required variable with no value. Fix the reported problem in the Compose file and reopen the tab.
|
||||
</Accordion>
|
||||
<Accordion title="I cannot change the exposure intent">
|
||||
Editing the intent needs stack edit access. With read access you can see the current classification but not change it.
|
||||
Editing the intent requires stack edit access. With read-only access you can see the current classification but not change it.
|
||||
</Accordion>
|
||||
<Accordion title="The runtime matches compose but I changed the Compose file">
|
||||
The drift comparison compares the declared model against what is currently running in Docker, not against what Docker would run if you deployed now. The runtime reflects the last deploy. Deploy the updated file to bring the runtime in line with the file, at which point the comparison clears.
|
||||
</Accordion>
|
||||
<Accordion title="I see a foreign network attachment I did not configure">
|
||||
A foreign network attachment means a container from this stack is connected to a network owned by a different stack. This can happen when two stacks share a network by each declaring it as `external:`, when you ran `docker network connect` manually, or when a previously external network changed ownership. Check the Compose files for both stacks and compare against what the Docker daemon shows with `docker network ls` and `docker network inspect`.
|
||||
</Accordion>
|
||||
<Accordion title="A service shows no network membership">
|
||||
A service with no explicit `networks:` block attaches to the stack's implicit default network, which Docker creates automatically. The service card shows no named network membership in that case. Services on the same default network can still communicate with each other using the service name as a hostname.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Compose Doctor" icon="stethoscope" href="/features/compose-doctor">
|
||||
Runs preflight checks against the same effective model, including the five exposure-aware findings that rely on the intents set in the Networking tab.
|
||||
</Card>
|
||||
<Card title="Drift Detection" icon="code-compare" href="/features/stack-drift">
|
||||
Shows the same four runtime drift types with a persistent history, so you can see when drift first appeared and when it cleared.
|
||||
</Card>
|
||||
<Card title="Resources Hub" icon="network-wired" href="/features/resources">
|
||||
Host-wide view of every Docker network and container attachment on the node, independent of which stack they belong to.
|
||||
</Card>
|
||||
<Card title="Environment and Secrets Guardrails" icon="shield-halved" href="/features/environment-guardrails">
|
||||
Documents which fields Sencho reads and which it redacts, and how to keep secrets safe from interpolation into structural fields.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,60 +1,121 @@
|
||||
---
|
||||
title: Storage Portability
|
||||
description: See every mount a stack depends on, learn whether it is portable or tied to this node, and find out what will break if you move or restore it, all before you deploy.
|
||||
description: See every mount a stack depends on, understand whether it can move cleanly between nodes, and find out what you need to back up before you move or restore it.
|
||||
---
|
||||
|
||||
The **Storage** tab in the right-hand **Anatomy** panel answers a Compose-first question: *what storage does this stack depend on, and what happens to it if I move or restore the stack?* It renders the effective Compose model, the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied, lists every mount, and gives the stack a single portability verdict.
|
||||
The **Storage** tab in the right-hand **Anatomy** panel answers a Compose-first question before you deploy, move, or restore a stack: *what storage does this stack depend on, and is it tied to this node?* It renders the effective Compose model (the fully resolved result after interpolation, includes, profiles, `.env`, and `env_file` are applied), lists every mount, and gives the stack a single portability verdict with the specific reasons behind it.
|
||||
|
||||
The view is read-only with respect to the stack and the host: it never changes a mount, and it never reads, moves, or changes the ownership of any file. It inspects structure only, mount type, source and target paths, the read-only flag, and a host path's existence, type, and owner, so nothing inside a volume or bind mount is ever opened. One caveat: a bind source or target assembled from a secret `${VAR}` is resolved before the model is read, so its value does show. Keep secrets in `environment:` or `env_file:` rather than interpolating them into mount paths. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
The view is read-only. It never changes a mount, and it never reads, moves, or changes the ownership of any file. It inspects structure only: mount type, source and target paths, the read-only flag, and for bind mounts inside the stack directory, whether the path exists, its type, and its POSIX owner. One caveat: a bind source that interpolates a secret from `${VAR}` is resolved before the model is read, so that value will appear. Keep secrets in `environment:` or `env_file:` rather than interpolating them into mount paths. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
|
||||
## Where to find it
|
||||
|
||||
Click any stack in the left sidebar, then switch to the **Storage** tab in the Anatomy panel header.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-storage/storage-tab.png"
|
||||
alt="The Anatomy panel with the Storage tab selected, showing the STORAGE PORTABILITY label, a node-bound verdict card with the reason that a bind path outside the stack directory must exist on every target node, and a SNAPSHOT COVERAGE section below"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
The tab only appears when the active node reports that it supports storage portability. A node running an older version does not advertise this capability, so the tab stays hidden until that node is updated.
|
||||
|
||||
## Storage inventory
|
||||
|
||||
Each service lists its mounts, grouped by service:
|
||||
Every service is listed under its own heading. For each mount, Sencho shows:
|
||||
|
||||
- **bind** mounts a host path into the container. For a path inside the stack directory, Sencho also shows whether it exists, whether it is a file, directory, socket, or symlink, and its owner on Linux hosts.
|
||||
- **named** uses a Docker-managed volume. An **external** chip marks a volume the stack expects to already exist on the node.
|
||||
- **anonymous** is an unnamed volume. Because it has no name, it is easy to miss when backing up and is orphaned when the container is recreated.
|
||||
- **tmpfs** is an in-memory mount that holds nothing on disk.
|
||||
- A **socket** chip marks a mount of the Docker socket.
|
||||
- A **type chip**: `bind`, `named`, `anonymous`, `tmpfs`, or `socket` (when the Docker socket is mounted).
|
||||
- An **access chip**: `ro` when the mount is read-only, `rw` when it is read-write.
|
||||
- An **external chip** (warning color) when a named volume declares `external: true` in the Compose file. This means Docker expects the volume to already exist on the node; Sencho will not create it on deploy.
|
||||
- For bind mounts outside the stack directory: the word `external` in muted text, indicating Sencho cannot verify the path because it is outside the area it can see.
|
||||
- For bind mounts inside the stack directory: an inline status word (`file`, `directory`, `socket`, `symlink`, or `missing`) and on Linux hosts the owner as `uid X:gid Y`.
|
||||
- If a bind source inside the stack directory is a symlink that resolves outside the directory: `symlink escapes` in muted text.
|
||||
- The source path and target path: `source → target`.
|
||||
|
||||
Every mount also shows whether it is **read-only** or **read-write**.
|
||||
If the stack declares no mounts at all, the inventory shows a single card: "This stack declares no mounts."
|
||||
|
||||
## Portability
|
||||
## Portability verdict
|
||||
|
||||
The top of the tab gives the stack one verdict, with the reasons behind it:
|
||||
The top of the tab gives the stack a single verdict. Below the verdict chip, Sencho lists every specific reason that contributed to it, so you know exactly what is tying the stack to the node or limiting its portability.
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| **Portable** | Every mount is inside the stack directory, or there is no persistent storage, so the stack moves cleanly with its files. |
|
||||
| **Partially portable** | The Compose structure moves cleanly, but named or anonymous volume data lives on this node and is not carried by moving the files. Back that data up separately. |
|
||||
| **Node-bound** | The stack binds host paths outside its directory, or mounts the Docker socket, so it is tied to this specific host. Those paths must exist on any node you move it to. |
|
||||
| **Unknown** | The effective model could not be rendered, so portability cannot be determined. |
|
||||
| Verdict | Meaning |
|
||||
|---------|---------|
|
||||
| **Portable** | Every bind mount resolves inside the stack directory (or there are no mounts at all). The stack moves cleanly with its files. |
|
||||
| **Partially portable** | The Compose files move cleanly, but one or more named or anonymous volumes hold data on this node. That data does not travel with the files when you move the stack. Back it up separately. |
|
||||
| **Node-bound** | The stack binds host paths outside the stack directory, mounts the Docker socket, or has a bind source that is a symlink pointing outside the stack directory. All of these require the relevant path or socket to exist on every node you move this stack to. |
|
||||
| **Unknown** | Sencho could not render the effective Compose model, so portability cannot be determined. |
|
||||
|
||||
A read-only bind to an outside path is still node-bound: read-only lowers the risk of a change, not the need for the path to exist on the target. A bind that is a symlink pointing outside the stack directory is treated as node-bound as well.
|
||||
A few things worth knowing:
|
||||
|
||||
- A read-only bind to an outside path is still **node-bound**. The `ro` flag lowers the risk of an accidental write, not the requirement that the path exist on the target node.
|
||||
- A symlink inside the stack directory that resolves outside it is treated as **node-bound** for the same reason: moving the stack files does not bring the symlink target with them.
|
||||
- For **partially portable** stacks, anonymous volumes are the most common oversight. Because they have no name, they are easy to miss when taking a manual backup and are orphaned when the container is recreated.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/compose-storage/storage-node-bound.png"
|
||||
alt="The Storage panel for a stack that mounts the Docker socket, showing a node-bound verdict card with the reason 'Service mounts the Docker socket, tying the stack to this host's Docker engine', followed by the socket mount row with SOCKET, RW, and external chips and the path /var/run/docker.sock"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Snapshot coverage
|
||||
|
||||
A stack with persistent storage is only as safe as its backups. Sencho shows administrators whether a recent fleet snapshot covers the stack, and warns when a stack that holds data has not been snapshotted in the last seven days.
|
||||
Persistent storage is only as safe as its backups. Sencho shows administrators whether a recent fleet snapshot covers the stack:
|
||||
|
||||
Fleet snapshots capture Compose and env files, not the data inside named volumes or bind mounts. The tab states this plainly so a snapshot is never mistaken for an application-data backup. Back up volume data separately before moving or restoring a stack.
|
||||
- When the stack has persistent storage and has not been included in a fleet snapshot in the last 7 days, a **warning card** appears with a "Take a fleet snapshot" link. On remote nodes the link is hidden; take the snapshot from the Fleet view on the hub instead.
|
||||
- When a recent snapshot exists, the section shows the relative time: "Last fleet snapshot 3d ago."
|
||||
- The section is visible to **administrators only**. Other roles see only the caveat below.
|
||||
|
||||
The section always displays a reminder: **fleet snapshots capture Compose and env files, not the data inside named volumes or bind mounts.** A snapshot is not a substitute for a data backup. Back up volume data separately before moving or restoring a stack.
|
||||
|
||||
## Findings in Doctor
|
||||
|
||||
Storage risk findings live in the **Doctor** tab, which reads the same model: a missing bind-mount path, a mount whose ownership is likely to mismatch the container user, a mounted Docker socket, a required external volume that does not exist, and an anonymous volume whose data has no name to back up by.
|
||||
Storage risk findings appear in the **Doctor** tab, which reads the same effective model. Sencho checks for:
|
||||
|
||||
- A bind-mount source path that is missing on the current node.
|
||||
- A bind mount whose file owner is likely to mismatch the user the container runs as.
|
||||
- A mount of the Docker socket.
|
||||
- An `external: true` named volume that does not yet exist on the node.
|
||||
- An anonymous volume whose data has no name to identify it by when backing up.
|
||||
|
||||
See [Compose Doctor](/features/compose-doctor) for the full rule reference and how to run a preflight check.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The Storage tab is not there">
|
||||
The Storage tab appears on nodes that support storage portability. A node that does not advertise the capability does not show the tab.
|
||||
<Accordion title="The Storage tab is not visible">
|
||||
The Storage tab appears only when the active node reports that it supports this feature. A node running an older version of Sencho does not advertise the capability, so the tab is hidden for that node until it is updated.
|
||||
</Accordion>
|
||||
<Accordion title="A bind path shows no existence or owner">
|
||||
Existence, type, and owner are resolved only for paths inside the stack directory. A host path elsewhere on the machine is outside Sencho's view, so it is listed but left unverified, and it counts toward a node-bound verdict.
|
||||
<Accordion title="A bind mount shows no existence status or owner">
|
||||
Existence, type, and owner are resolved only for bind sources that sit inside the stack directory. A path anywhere else on the host is outside Sencho's scope, so it is listed as `external` but left unverified. It still counts toward a node-bound verdict because it must exist on any node you move the stack to.
|
||||
</Accordion>
|
||||
<Accordion title="It says the model cannot render">
|
||||
`docker compose config` could not produce an effective model, usually a YAML error, an unresolved include or merge, or a required variable with no value. Fix the reported problem and reopen the tab.
|
||||
<Accordion title="A named volume shows an external chip">
|
||||
The orange `external` chip on a named volume means the Compose file declares `external: true` for that volume. Docker expects the volume to already exist on the node and will not create it on deploy. If the volume does not exist, the deploy will fail. The [Compose Doctor](/features/compose-doctor) external-volume-missing rule catches this before you deploy.
|
||||
</Accordion>
|
||||
<Accordion title="There is no snapshot coverage shown">
|
||||
Snapshot coverage is shown to administrators. The warning and the last-snapshot line appear for a stack that holds persistent data once a snapshot has run on its node.
|
||||
<Accordion title="The panel says it cannot render the model">
|
||||
Sencho calls `docker compose config` to produce the effective model. If that fails, the panel shows a red card with a specific message. Common causes are a YAML syntax error, an unresolved `include` or merge key, or a required variable with no value. Sencho names any missing required variables by name. Fix the Compose or env file and reopen the tab.
|
||||
</Accordion>
|
||||
<Accordion title="Snapshot coverage is not shown">
|
||||
The snapshot coverage section is visible to administrators only. If you are signed in as an administrator and the coverage line is not shown, no fleet snapshot has run yet on this node. Take an initial snapshot from the Fleet view to establish baseline coverage.
|
||||
</Accordion>
|
||||
<Accordion title="The 'Take a fleet snapshot' link is not there">
|
||||
The link only appears on local nodes. When the active node is a remote proxy, initiate the snapshot from the Fleet view on the hub, where the full fleet action controls are available.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Compose Doctor" icon="stethoscope" href="/features/compose-doctor">
|
||||
Run a preflight check that surfaces storage risks as actionable findings before you deploy.
|
||||
</Card>
|
||||
<Card title="Fleet Snapshots" icon="camera" href="/features/fleet-backups">
|
||||
Back up Compose and env files across all nodes in one action. Covers configuration, not volume data.
|
||||
</Card>
|
||||
<Card title="Files and Volumes" icon="folder-open" href="/features/stack-file-explorer">
|
||||
Browse and edit the files inside a stack directory and read the contents of named volumes.
|
||||
</Card>
|
||||
<Card title="Environment Guardrails" icon="key" href="/features/environment-guardrails">
|
||||
Keep secrets out of mount paths so they are not exposed in the storage inventory.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -5,18 +5,22 @@ description: "Block deploys that violate a scan policy before docker compose up
|
||||
|
||||
Deploy enforcement is the pre-flight half of Sencho's vulnerability workflow. When a [scan policy](/features/vulnerability-scanning#scan-policies) with **Block on deploy** enabled matches a stack, Sencho scans every image referenced by the stack's compose file before starting any container. A policy gates on exploitation risk: a known-exploited CVE (CISA KEV), a fixable Critical/High finding, or a raw severity threshold. If any image matches an enabled condition, the deploy is rejected and the stack never starts. Detection always continues post-deploy and on a schedule, so images that develop new vulnerabilities after the initial deploy still surface through alerts.
|
||||
|
||||
<Note>
|
||||
Deploy enforcement and scan policies require an **Admiral** license.
|
||||
</Note>
|
||||
|
||||
## Configuring a block policy
|
||||
|
||||
Policies are managed on the **Security** page → **Policies** tab. The **Add policy** button opens the editor; existing policies appear as a list of cards with a badge per active block condition (`KEV`, `Fixable`, and `max: <SEVERITY>` when the severity threshold is on), a `block` badge, the configured stack-pattern scope, and pencil and trash buttons.
|
||||
Policies are managed on the **Security** page → **Policies** tab. The **Add policy** button opens the editor. When no policies exist, an empty-state callout reads "No scan policies configured" with a prompt to add one. Existing policies appear as a list of cards: each card shows the policy name, a badge per active block condition (`max: <SEVERITY>` when the severity threshold is on, `KEV`, and `Fixable`), a destructive `block` badge when the pre-flight gate is active, and a `disabled` badge when the policy is off. Below the name, the card shows `Scope:` followed by the stack-pattern glob in monospace, or `all stacks` in italics when no pattern is set. Pencil and trash buttons appear for admins on control nodes.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-enforcement/policy-list.png" alt="Scan Policies card showing a configured policy with the name 'Production block on critical', a max: CRITICAL badge, a block badge, and the Scope demo-blocked-* set as the stack pattern" />
|
||||
<img src="/images/deploy-enforcement/policy-list.png" alt="Policies tab showing a policy named 'Production block on critical' with max: CRITICAL, KEV, Fixable, and block badges and Scope: prod-*, with pencil and trash action buttons to the right. The Honor suppressions in deploy blocks toggle sits below the policy card." />
|
||||
</Frame>
|
||||
|
||||
The editor exposes the fields that govern enforcement:
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-enforcement/policy-edit-modal.png" alt="New policy modal with kicker SECURITY · NEW POLICY, fields for Name, Stack pattern, the Block conditions group (Severity threshold, Known-exploited (KEV), Fixable Critical/High), a Block on deploy toggle, and an Enabled toggle" />
|
||||
<img src="/images/deploy-enforcement/policy-edit-modal.png" alt="New policy modal with kicker SECURITY · NEW POLICY and title New policy. Fields shown: Name filled with 'Production block on critical', Stack pattern (optional) empty, Block conditions section with Severity threshold toggled ON and the Critical severity dropdown visible, Known-exploited (KEV) ON, Fixable Critical/High ON, Block on deploy OFF, Enabled ON. Cancel and Create buttons at the bottom." />
|
||||
</Frame>
|
||||
|
||||
| Field | Purpose |
|
||||
@@ -31,6 +35,10 @@ New policies default to a risk-first posture: known-exploited and fixable condit
|
||||
|
||||
The editor sets the pattern, the block conditions, and the toggles. Per-node scoping is set via the [Security API](/api-reference/security#scan-policies) (`node_id`) or replicated from a control node via [Fleet Federation](/features/fleet-federation). When more than one enabled policy matches a stack on the target node, a node-scoped policy wins over a fleet-wide one, a policy with a stack pattern wins over a catch-all, and the lowest-numbered policy breaks any remaining tie.
|
||||
|
||||
### Honor suppressions
|
||||
|
||||
The **Honor suppressions in deploy blocks** toggle sits at the bottom of the **Policies** tab, below the policy list. When on, a [suppressed CVE](/features/cve-suppressions) no longer counts toward a block-on-deploy policy, so an accepted finding will not stop a deploy on this instance. When off (the default), policies evaluate the raw scan result and block on any finding that matches a condition, including those you have suppressed elsewhere.
|
||||
|
||||
## How enforcement runs
|
||||
|
||||
Sencho applies the pre-flight gate on every code path that can start a compose stack:
|
||||
@@ -60,14 +68,15 @@ Pre-flight scans use the same 24-hour digest cache as on-demand scans, so the se
|
||||
## What the block dialog shows
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-enforcement/block-dialog.png" alt="Deploy blocked dialog with kicker DEMO-BLOCKED-APP · SCAN POLICY · BLOCKED, title 'Deploy blocked by security policy', a row listing the offending image redis:7.0-alpine with 8 critical · 59 high counts and a CRITICAL severity chip, a Close button, and a destructive Deploy anyway button" />
|
||||
<img src="/images/deploy-enforcement/block-dialog.png" alt="Deploy blocked dialog with kicker PROFILARR · SCAN POLICY · BLOCKED, title 'Deploy blocked by security policy', a description naming the policy and block conditions, a violation row for santiagosayshey/profilarr:latest with 16 CRITICAL · 111 HIGH · 59 FIXABLE counts and Severity and Fixable reason badges and a CRITICAL severity chip, and Close and Deploy anyway buttons." />
|
||||
</Frame>
|
||||
|
||||
The dialog shows:
|
||||
|
||||
- A kicker with the stack name, the words **SCAN POLICY**, and **BLOCKED** in the destructive accent color.
|
||||
- The policy name and a sentence naming the conditions the policy blocks on.
|
||||
- One row per offending image, with the image reference in monospace, the counts of critical and high findings (plus known-exploited and fixable counts when present), a badge for each matched condition, and a severity chip in the matching color.
|
||||
- The policy name and a sentence naming every condition the policy blocks on.
|
||||
- One row per offending image, with the image reference in monospace, the counts of critical and high findings (plus known-exploited and fixable counts when present), a badge for each matched condition (`Severity`, `KEV`, or `Fixable`), and a severity chip in the matching color.
|
||||
- If an image could not be scanned rather than failing a policy check, the row shows a **Could not be scanned** subtitle and the scan error text. A note below the list reads "The deploy was blocked because the scan did not complete. Resolve the issue above and deploy again, or bypass if you accept the risk."
|
||||
- A **Close** button that dismisses the dialog without deploying.
|
||||
- A destructive **Deploy anyway** button when the current user is an admin (see bypass below). For non-admin sessions the primary slot is replaced by a disabled outline button labeled **Admin required to bypass**.
|
||||
|
||||
@@ -92,6 +101,14 @@ API callers can pass `?ignorePolicy=true` on any deploy endpoint to request a by
|
||||
|
||||
The auto-update scheduler runs deploys without an interactive user, so the bypass flag does not apply. When a scheduled auto-update is rejected by a policy, Sencho dispatches a `scan_finding` warning alert with the stack name, the policy name, and the offending images, then skips that stack and continues the rest of the schedule. Re-enabling that stack in auto-update means either bringing the image down to compliant severity or relaxing the policy. A scheduled auto-start that is rejected is reported as a failed run with the same warning alert.
|
||||
|
||||
## Fleet policy replication
|
||||
|
||||
Scan policies are evaluated on the node that runs the deploy, so the **Policies** tab always manages the local Sencho instance. When you view a remote node's Security page through the fleet hub, the Policies section shows a "Managed on the local instance" notice; switch to that node's own UI to manage its policies.
|
||||
|
||||
When a node joins [Fleet Federation](/features/fleet-federation) as a replica, its scan policies and CVE suppressions are mirrored from the control node. The Policies tab on a replica shows a **Managed by control node** banner in place of the **Add policy** button. Admins can view the replicated policies for audit but cannot edit them directly; edits must be made on the control node, where they propagate to all replicas.
|
||||
|
||||
The **Demote to control** button in the replica banner removes every replicated policy and CVE suppression from the instance and re-enables local policy editing. This action is irreversible and requires confirmation. After demotion the node operates as an independent control with no policies until new ones are added.
|
||||
|
||||
## Drift detection keeps running
|
||||
|
||||
Deploy enforcement prevents a new deploy from introducing known vulnerabilities at the front door. Long-running stacks whose images were clean at deploy time can still develop new CVEs as upstream feeds update. Two surfaces catch this:
|
||||
@@ -129,4 +146,7 @@ Neither drift mechanism blocks, stops, or quarantines a running stack automatica
|
||||
<Accordion title="I want a block policy that only applies to a subset of my fleet">
|
||||
Combine two mechanisms: scope the policy to a specific node (policies scoped to a node win over global ones) and tighten the stack-pattern glob. For example, a policy with `stack_pattern=prod-*` scoped to your production node fires only on `prod-*` stacks deployed to that node.
|
||||
</Accordion>
|
||||
<Accordion title="The Policies tab is read-only on this node">
|
||||
This occurs when the node is enrolled in Fleet Federation as a replica. The **Managed by control node** banner confirms this state; the policy list is visible for audit but edits are locked. To manage policies, either open the control node and edit them there (changes propagate to all replicas automatically), or click **Demote to control** to disconnect this node from federation and re-enable local editing, which removes all replicated policies and CVE suppressions from this instance.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Deploy Progress
|
||||
description: Stream live output from stack deploy, install, update, restart, and stop operations in a structured log view.
|
||||
description: Stream live output from stack deploy, update, install, restart, stop, and scan operations with a structured log view, a minimizable pill, and an optional inline status band.
|
||||
---
|
||||
|
||||
When you trigger a stack action that runs through `docker compose` (Deploy, Update, Install from the App Store, Apply with deploy from a Git Source), a progress modal opens and streams the output in real time. Each line is parsed into a timestamped row with a stage badge so you can track the deployment lifecycle as it runs. The modal can be minimized to a small pill that follows you across navigation, so you can leave the App Store mid-install and still see the status from any screen. If an operation goes quiet or fails, Sencho keeps the state observable: the modal warns when output has stopped, and the stack page surfaces recovery actions you can take without leaving Sencho.
|
||||
When you trigger a stack operation that runs through `docker compose` or the Docker Engine, Sencho opens a live progress view that streams output as it happens. Each line becomes a timestamped row with a stage badge so you can track the deployment lifecycle as it runs. The progress view appears as a centered modal overlay by default or as a quiet status band on the stack detail when you prefer a less intrusive style. Either way, the view can be minimized to a floating pill that follows you across navigation so you can leave the App Store mid-install and still see the status from any screen.
|
||||
|
||||
## Showing, hiding, and styling deploy progress
|
||||
|
||||
@@ -15,25 +15,46 @@ Deploy progress is **on by default**. To run operations without it, open **Setti
|
||||
The choices are saved to the current browser only and synced across tabs in the same browser without a reload.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/setting-toggle.png" alt="Settings Stacks panel with the deploy progress toggle." />
|
||||
<img src="/images/deploy-progress/setting-toggle.png" alt="Settings Stacks panel showing the Deploy progress toggle set to Enabled, the Progress style selector with Modal selected, the Observe health after updates toggle, and the Observation window field." />
|
||||
</Frame>
|
||||
|
||||
## Using the modal
|
||||
|
||||
The modal opens automatically when you trigger an action. It floats centered in the viewport and does not block access to the rest of the UI.
|
||||
The modal opens automatically when you trigger an action. It floats centered in the viewport and does not block the rest of the UI.
|
||||
|
||||
### What the modal shows
|
||||
|
||||
- **Header**: the action verb (Deploying, Updating, Installing, Restarting, Stopping), the stack name in monospace truncated at 200 px, and an elapsed-time chip that appears once the connection moves past the initial **Connecting...** state.
|
||||
- **Header**: the action verb (Deploying, Updating, Installing, Restarting, Stopping, Scanning), the stack name in monospace truncated at 200 px, and an elapsed-time chip that appears once the connection moves past the initial **Connecting...** state.
|
||||
- **Status indicator** in the upper right: one of `Connecting...` while the stream attaches, a live `<n> lines` counter while output is flowing, `Succeeded · closes in <n>s` after a clean finish, or the failure message itself when the run errors out. If the live stream cannot attach or drops, the indicator switches to `Live progress unavailable` and the operation keeps running in the background.
|
||||
- **Structured log body**: one row per output line. Each row carries a timestamp, a fixed-width stage badge, and the log message. Error rows have a destructive left border and tinted background so they stand out without scanning.
|
||||
- **Footer**: a `Raw output` / `Hide raw` toggle on the left, with **Minimize** and **Close** buttons on the right.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/modal-streaming.png" alt="Deploy progress modal mid-flight, showing the elapsed timer, a live line count in the header, and a populated body of timestamped LOG rows from a docker compose pull" />
|
||||
<img src="/images/deploy-progress/modal-streaming.png" alt="Deploy progress modal mid-flight, showing the elapsed timer, a live line count in the status area, and a populated body of timestamped LOG rows from a docker compose pull." />
|
||||
</Frame>
|
||||
|
||||
### Stage badges
|
||||
## Health gate
|
||||
|
||||
After a deploy or update completes, Sencho keeps watching the stack's containers for the configured observation window (90 seconds by default) before issuing a final success verdict. This observation period is the health gate. The modal withholds the success state and auto-close during observation and shows a banner describing the current verdict:
|
||||
|
||||
| State | Banner text |
|
||||
|-------|-------------|
|
||||
| Observing | `Health gate: observing containers (Xs of Ys). Closing this panel does not stop the observation.` where X is the elapsed seconds and Y is the configured window. |
|
||||
| Passed | `Health gate passed: containers stayed healthy through the observation window.` |
|
||||
| Failed | `Health gate failed: <reason>. Rollback options are available on the stack.` |
|
||||
| Unknown | `Health gate result is unknown: <reason>. Check the stack's containers directly.` |
|
||||
|
||||
When the gate passes, the success state and 4-second auto-close resume. When the gate fails or returns an unknown result, the modal stays open with the reason visible and must be dismissed manually.
|
||||
|
||||
Closing or minimizing the modal never stops the observation. The gate runs to completion in the background regardless of what happens to the progress view.
|
||||
|
||||
The health gate activates only for **deploy** and **update** actions. Restart, stop, and scan do not trigger it.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/modal-health-gate.png" alt="Deploy progress modal showing the health gate observing banner with an elapsed counter, the green health gate passed banner below it, and the full structured log body." />
|
||||
</Frame>
|
||||
|
||||
## Stage badges
|
||||
|
||||
Each log row is classified by content. Most lines render as **LOG** because the badges are gated on Docker Compose's `[+] Pulling` / `[+] Creating` / `[+] Starting` progress prefixes, which only appear when Compose is run in TTY mode. The full set of badges Sencho can render:
|
||||
|
||||
@@ -49,64 +70,72 @@ Each log row is classified by content. Most lines render as **LOG** because the
|
||||
| ERR | Error line (`Error response from daemon` or text starting with `error`) |
|
||||
| LOG | Default for any line that does not match the patterns above |
|
||||
|
||||
**ERR** rows pick up the destructive left border and the tinted background. **WARN** rows use a softer warning tint without the border.
|
||||
**ERR** rows get the destructive left border and the tinted background. **WARN** rows use a softer warning tint without the border.
|
||||
|
||||
### Raw output toggle
|
||||
## Raw output
|
||||
|
||||
Click **Raw output** in the footer to expand a 200 px raw terminal panel beneath the structured rows. The raw view shows the unprocessed compose stream, including the progress bars and ANSI-formatted output that the structured parser does not render. Clicking the toggle a second time (now labelled **Hide raw**) collapses the panel.
|
||||
Click **Raw output** in the footer to expand a raw terminal panel beneath the structured rows. The raw view shows the unprocessed compose stream, including the progress bars and ANSI-formatted output that the structured parser does not render. Clicking the toggle a second time (now labelled **Hide raw**) collapses the panel.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/modal-raw-output.png" alt="Deploy progress modal with the Raw output panel expanded, showing structured rows above and the green-on-black raw terminal stream below" />
|
||||
<img src="/images/deploy-progress/modal-raw-output.png" alt="Deploy progress modal with the Raw output panel expanded, showing structured rows above and the green-on-black raw terminal stream below." />
|
||||
</Frame>
|
||||
|
||||
### Auto-close on success
|
||||
## Auto-close on success
|
||||
|
||||
When an action completes successfully, the status indicator switches to `Succeeded · closes in <n>s` and a 4-second countdown begins. Hover anywhere over the modal to pause the countdown; moving the cursor away restarts a fresh 4 seconds. Clicking **Close** dismisses the modal immediately.
|
||||
|
||||
When the [health gate](/features/health-gated-updates) is observing the stack after a deploy or update, the modal withholds the success verdict: the status indicator shows **Verifying health** instead of **Succeeded**, and the auto-close waits for the gate. A passed gate shows the success state and resumes the countdown; a failed or unknown verdict becomes the headline result and keeps the modal open with the reason. Closing the modal never stops the observation.
|
||||
When the [health gate](#health-gate) is observing the stack after a deploy or update, the modal withholds the success verdict: the status indicator shows **Verifying health** instead of **Succeeded**, and the auto-close waits for the gate result. A passed gate resumes the countdown; a failed or unknown result keeps the modal open until you close it manually.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/modal-succeeded.png" alt="Deploy progress modal in the succeeded state with a green checkmark, the text 'Succeeded' and 'closes in 2s' in the header, and the full structured log body visible beneath" />
|
||||
<img src="/images/deploy-progress/modal-succeeded.png" alt="Deploy progress modal in the succeeded state with a green checkmark, the text 'Succeeded closes in 0s' in the header, the health gate passed banner below it, and the full structured log body visible." />
|
||||
</Frame>
|
||||
|
||||
### Stalled-output warning
|
||||
## Stalled-output warning
|
||||
|
||||
A long pull or recreate can go quiet for a stretch. When an operation is still running but has produced no new output for a while, the modal shows a warning strip with the elapsed quiet time and the last line received (or a note that no output has arrived yet). The operation keeps running; the strip only makes the quiet visible so you are not left guessing whether anything is happening. If a step is genuinely hung, Sencho stops it after a longer idle window and shows a failure with recovery actions (see [Recovery actions](#recovery-actions)).
|
||||
A long pull or recreate can go quiet. When an operation is still running but has produced no new output for 75 seconds, the modal shows a warning strip with the elapsed quiet time and the last line received (or a note that no output has arrived yet). The operation keeps running; the strip only makes the quiet visible so you know something is still in progress.
|
||||
|
||||
### On failure
|
||||
## On failure
|
||||
|
||||
If the action fails, the modal stays open. The status indicator switches to a destructive icon and shows the error message itself, truncated to 200 px in the header with the full text available on hover. Error rows in the body pick up the destructive left border so they are easy to find when scrolling. The modal stays open until you click **Close**. Close it to reveal the recovery panel on the stack page.
|
||||
If the action fails, the modal stays open until you click **Close**. The status indicator switches to a destructive icon and shows the error message itself, truncated to 200 px in the header with the full text available on hover. Error rows in the body get the destructive left border so they are easy to find when scrolling. Close the modal to reveal the recovery panel on the stack page.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/modal-failed.png" alt="Deploy progress modal in the failed state, with the truncated error message visible in the header and several ERR rows highlighted with a destructive border in the body" />
|
||||
<img src="/images/deploy-progress/modal-failed.png" alt="Deploy progress modal in the failed state, with the truncated error message visible in the header and several ERR rows highlighted with a destructive border in the body." />
|
||||
</Frame>
|
||||
|
||||
### Minimize to pill
|
||||
## Minimize to pill
|
||||
|
||||
Click **Minimize** to collapse the modal to a small status pill anchored at the bottom center of the viewport. The pill shows the action verb, the stack name in monospace, and a status dot that animates while the run is in flight (brand color, pulsing) and goes solid green or red at completion. Click the pill to expand the modal back.
|
||||
Click **Minimize** to collapse the modal to a small status pill anchored at the bottom center of the viewport. The pill shows the action verb, the stack name in monospace, and a status dot that pulses with the brand color while the run is in flight and goes solid green or red at completion. Click the pill to expand the modal back.
|
||||
|
||||
The pill is mounted as a portal, so it persists across navigation: leave the App Store mid-install and the pill follows you to the dashboard, the editor, or any other view.
|
||||
|
||||
Minimizing, closing, or navigating away never cancels the operation. The progress view only displays output; the deploy, update, or stop runs to completion on its own. If you dismiss the view while live output is flowing, the run still finishes in the background, and its success or failure lands in your notifications.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/pill.png" alt="Minimized deploy progress pill anchored at the bottom center of a stack editor view, showing the brand-colored pulsing dot and the text 'Updating docs-demo'" />
|
||||
<img src="/images/deploy-progress/pill.png" alt="Minimized deploy progress pill anchored at the bottom center of the dashboard, showing the brand-colored pulsing dot and the text 'Updating dozzle'." />
|
||||
</Frame>
|
||||
|
||||
## Inline style
|
||||
|
||||
With **Progress style** set to **Inline**, the modal does not open on its own. Instead, the stack detail shows a compact status band for the running operation, between the action buttons and the container list (a status card on a phone). The band shows:
|
||||
|
||||
- the operation and its elapsed time,
|
||||
- the current phase while images pull and containers recreate,
|
||||
- the latest output line,
|
||||
- the post-update [health gate](/features/health-gated-updates) result once the operation succeeds.
|
||||
- a pulsing dot (brand color while running, green on success),
|
||||
- the operation verb and elapsed time,
|
||||
- the current status: the active compose phase while the operation is in progress, `Verifying health` during the health gate, `Health gate passed` or `Health check unknown` after the gate settles, or `Live progress unavailable` when the live stream cannot attach,
|
||||
- the latest output line beneath the status (while the operation is running),
|
||||
- a **View output** button to open the full log modal on demand,
|
||||
- a dismiss button to clear the band.
|
||||
|
||||
A **View output** button opens the full log modal on demand, and a dismiss control clears the band. When the operation finishes cleanly, the elapsed time freezes at its final duration and the band clears itself a few seconds later. Closing the opened log modal in this style only hides it again; the band stays until the operation is done or you dismiss it. If the live stream cannot attach or drops, the band shows the same **Live progress unavailable** state and the operation keeps running in the background.
|
||||
When the operation finishes cleanly and the health gate (if any) has passed, the band clears itself 4 seconds later. Closing the opened log modal in this style only hides it again; the band stays until the operation is done or you dismiss it. If a health gate fails, the band steps aside and the recovery surface on the stack page takes over.
|
||||
|
||||
When you navigate away from the stack detail while an operation is running, the band hands off to the floating pill so you always have a status surface.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/deploy-progress/inline-banner.png" alt="Stack detail in Inline progress style, showing the status band between the action buttons and the container list, with the pulsing dot, verb, elapsed time, latest output line, and View output button." />
|
||||
</Frame>
|
||||
|
||||
## Recovery actions
|
||||
|
||||
When a deploy or update fails, times out, or its outcome is ambiguous, the stack page offers safe next steps so you can fix the stack in place. On desktop a small **Update failed** chip appears in the stack card; click it to open a menu of recovery actions. On a phone the same actions show as an inline card on the stack detail. Either way it shows the failed action, the error, and how long the operation ran, and it works whether or not the progress modal is enabled.
|
||||
When a deploy or update fails, times out, or its outcome is ambiguous, the stack page offers safe next steps so you can fix the stack in place. On desktop a small **Update failed** chip appears in the stack card; click it to open a menu of recovery actions. On a phone the same actions show as an inline card on the stack detail. Either way, it shows the failed action, the error, and how long the operation ran, and it works whether or not the progress modal is enabled.
|
||||
|
||||
The available actions are:
|
||||
|
||||
@@ -120,13 +149,16 @@ Retry, Restart, and Roll back require deploy permission on the stack. After a fa
|
||||
|
||||
## Supported entry points
|
||||
|
||||
The modal opens for the following actions:
|
||||
The progress view opens for the following actions:
|
||||
|
||||
- **Deploy**, **Update**, **Restart**, **Stop** from the stack editor's action bar.
|
||||
- **Install** from the App Store.
|
||||
- **Apply** from a Git Source panel when the apply request includes a deploy.
|
||||
- **Scanning** a stack's image configuration.
|
||||
|
||||
Of those, **Deploy**, **Update**, **Install**, and Git **Apply** route through `docker compose up` and produce a populated structured-log body. **Restart** and **Stop** call the Docker Engine directly to act on existing containers; they bypass compose, so the modal opens, registers `0 lines`, and finishes. Useful as a confirmation surface for those actions, but with no log content.
|
||||
Of those, **Deploy**, **Update**, **Install**, and Git **Apply** route through `docker compose up` and produce a populated structured-log body. **Restart** and **Stop** call the Docker Engine directly to act on existing containers; they bypass compose, so the modal opens, registers `0 lines`, and finishes. **Scanning** streams its own output as LOG rows.
|
||||
|
||||
The [health gate](#health-gate) activates only after **Deploy** and **Update**; it does not fire for Restart, Stop, Install, Git Apply, or Scanning.
|
||||
|
||||
The HTTP API also exposes a `down` action (compose-level teardown) that streams its output the same way Deploy and Update do, but no UI control currently triggers it; the `down` endpoint is reachable from automation and from Sencho's own internal cleanup paths.
|
||||
|
||||
@@ -149,9 +181,32 @@ The HTTP API also exposes a `down` action (compose-level teardown) that streams
|
||||
Some compose wrappers and Docker plugins emit non-standard output. The parser falls back to a **LOG** row for any line it cannot classify, but if a line is consumed entirely by ANSI control sequences it can drop out of the structured view. Toggle **Raw output** to see the full stream.
|
||||
</Accordion>
|
||||
<Accordion title="An update stalled or appears stuck">
|
||||
If a pull or recreate produces no output for a while, the modal shows a stalled-output warning so you know the operation has gone quiet. The operation keeps running. If a step is genuinely hung, Sencho stops it after a longer idle window (10 minutes by default) and the stack page offers recovery actions where you can retry, restart, roll back when a backup exists, refresh the container state, or copy troubleshooting details. To change how long Sencho waits before treating a silent step as stalled, set `SENCHO_COMPOSE_STALL_TIMEOUT_MS`; raise it on slow links or for heavy local image builds.
|
||||
If a pull or recreate produces no output for 75 seconds, the modal shows a stalled-output warning so you know the operation has gone quiet. The operation keeps running in the background. If a step is genuinely hung, use the recovery panel on the stack page to retry, restart, roll back when a backup exists, refresh the container state, or copy troubleshooting details.
|
||||
</Accordion>
|
||||
<Accordion title="The health gate is blocking the modal from closing">
|
||||
The modal withholds auto-close while the health gate is observing containers and whenever the gate result is failed or unknown. A failed or unknown gate means the containers did not stay healthy through the observation window; the modal stays open so you can see the reason. Use the recovery actions on the stack page to retry, restart, or roll back. Clicking **Close** on the modal dismisses it manually at any time; it does not cancel the gate observation if it is still running.
|
||||
</Accordion>
|
||||
<Accordion title="No health gate appeared after a restart or scan">
|
||||
The health gate only fires after **deploy** and **update** actions. Restart, stop, install, git apply, and scan complete without a post-operation observation period. If you want health monitoring after a restart, deploy or update the stack instead.
|
||||
</Accordion>
|
||||
<Accordion title="I changed the deploy progress setting but it did not take effect">
|
||||
The settings live in `localStorage`: the on/off toggle under `sencho.deploy-feedback.enabled` and the Modal/Inline choice under `sencho.deploy-feedback.style`. Both apply to the current tab without a reload, and other tabs in the same browser pick up the change through a `storage` event. Deploy progress defaults to on in Modal style, so only an explicit choice changes it. If a tab still does not honour the setting, refresh that tab. The settings do not sync across browsers or devices; each one carries its own choice.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Health-gated updates" icon="heart-pulse" href="/features/health-gated-updates">
|
||||
Configure the observation window and understand how the gate classifies a passed, failed, or unknown result.
|
||||
</Card>
|
||||
<Card title="Stack activity" icon="clock-rotate-left" href="/features/stack-activity">
|
||||
Every deploy, update, and health gate verdict is recorded in the stack's activity timeline.
|
||||
</Card>
|
||||
<Card title="Deploy enforcement" icon="shield-check" href="/features/deploy-enforcement">
|
||||
Set risk policies that block or require review before a deploy or update can start.
|
||||
</Card>
|
||||
<Card title="Atomic deployments" icon="rotate" href="/features/atomic-deployments">
|
||||
How Sencho backs up compose files before each update and rolls back automatically on failure.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -65,9 +65,26 @@ When the container has a compose service name attached, an extra `⋮` button ap
|
||||
|
||||
## Anatomy panel
|
||||
|
||||
The right column shows the **Anatomy panel** by default: a read-only summary of the compose file, alongside tabs for the stack's activity timeline and its dossier.
|
||||
The right column shows the **Anatomy panel** by default: a read-only summary of the compose file alongside a scrollable tab row for other stack views.
|
||||
|
||||
The header strip carries three tabs (**Anatomy**, **Activity**, and **Dossier**) plus shortcuts to open the **files** tab and enter **edit** mode. The shortcuts belong to the strip itself and remain available regardless of which tab is active.
|
||||
<Frame>
|
||||
<img src="/images/editor/anatomy-tabs.png" alt="Anatomy panel header strip showing the Anatomy, Activity, Dossier, Drift, Environment, Networking, Doctor, and Storage tab row with Files and Edit shortcuts on the right" />
|
||||
</Frame>
|
||||
|
||||
The tab row always shows four tabs: **Anatomy**, **Activity**, **Dossier**, and **Drift**. Four more tabs appear when the active node advertises the matching capability.
|
||||
|
||||
| Tab | Always present? | What it shows |
|
||||
|-----|----------------|--------------|
|
||||
| **Anatomy** | Yes | Read-only compose file summary. |
|
||||
| **Activity** | Yes | Operational event timeline for this stack. See [Stack Activity](/features/stack-activity). |
|
||||
| **Dossier** | Yes | Exportable Markdown of the anatomy combined with operator notes. See [Stack Dossier](/features/stack-dossier). |
|
||||
| **Drift** | Yes | Live comparison of the declared compose against the running containers. See [Stack Drift](/features/stack-drift). |
|
||||
| **Environment** | When `env-inventory` capability is present | Variable inventory across all env files, with status for each variable. See [Environment Guardrails](/features/environment-guardrails). |
|
||||
| **Networking** | When `compose-networking` capability is present | Port exposure summary per service with intent classification. See [Compose Networking](/features/compose-networking). |
|
||||
| **Doctor** | When `compose-doctor` capability is present | Preflight check results grouped by severity. The tab gains a red dot for blocker findings and an amber dot for high-risk findings. See [Compose Doctor](/features/compose-doctor). |
|
||||
| **Storage** | When `compose-storage` capability is present | Mount inventory with portability assessment and snapshot coverage. See [Compose Storage](/features/compose-storage). |
|
||||
|
||||
The **Files** shortcut and the **Edit** button sit at the right end of the strip and stay available regardless of which tab is active.
|
||||
|
||||
The Anatomy tab lists:
|
||||
|
||||
@@ -81,12 +98,6 @@ The Anatomy tab lists:
|
||||
|
||||
When an image update is available, an inline banner appears at the top of the panel. Its tone follows the version-bump severity: `safe to apply` (patch), `review recommended` (minor), `breaking changes possible` (major), or `review required` when the bump cannot be classified. The banner has an inline **apply** button that runs the same operation as the action bar's **Update**; it is hidden for roles that lack the `stack:edit` permission and when the bump is flagged as blocked.
|
||||
|
||||
### Markdown export
|
||||
|
||||
The **Dossier** tab turns this same anatomy into an exportable Markdown document, combined with your own operator notes, with copy-to-clipboard and download actions. Empty sections render cleanly as *none*. The generated facts carry only variable **names** and **counts**, never the values stored in your `.env` file; your own notes are exported as written. See [Stack Dossier](/features/stack-dossier).
|
||||
|
||||
The **Activity** tab streams every operational event scoped to this stack. See [Stack Activity](/features/stack-activity) for the full event catalog and attribution rules.
|
||||
|
||||
## Editor mode
|
||||
|
||||
Clicking **edit** in the Anatomy strip swaps the right column for the Monaco editor card.
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
---
|
||||
title: Environment and Secrets Guardrails
|
||||
description: See every environment variable a stack uses, where it comes from, and whether it is likely a secret, without ever exposing a value. Sencho derives a per-stack env inventory, flags missing and duplicate variables, and can block a deploy when a required variable has no value.
|
||||
description: See every environment variable a stack uses, where it comes from, and whether it is likely a secret, without ever exposing a value. Sencho builds a per-stack inventory, shows env file status, flags missing and duplicate variables, and can block a deploy when a required variable has no value.
|
||||
---
|
||||
|
||||
The **Environment** tab in the right-hand **Anatomy** panel answers a question Compose makes surprisingly hard: *which environment variables does this stack actually use, where does each one come from, and is anything missing or sensitive?* Sencho derives the answer from the stack's Compose files and env files and presents it as a single inventory.
|
||||
The **Environment** tab in the right-hand **Anatomy** panel answers a question Compose makes surprisingly hard: which environment variables does this stack actually use, where does each one come from, and is anything missing or sensitive? Sencho derives the answer from the stack's Compose files and env files and presents it as a single inventory.
|
||||
|
||||
The inventory is advisory and read only. It never changes a stack, and it works entirely from variable **names**: a value is never read into the report, the checklist, or the logs, so nothing sensitive is exposed.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/environment-guardrails/env-overview.png" alt="Environment tab open on a stack. The panel shows a PROJECT ENVIRONMENT FILE section, a summary line reading '19 vars · 2 likely secret', an ENV FILES section with .env marked missing and a globals.env marked unverifiable, and a PRESENT group listing injected variables." />
|
||||
</Frame>
|
||||
|
||||
## Interpolation versus container injection
|
||||
|
||||
Compose treats environment in two distinct ways, and mixing them up is a common source of "it works on one host but not another":
|
||||
@@ -22,6 +26,12 @@ The inventory labels each variable with how it is used, so you can tell at a gla
|
||||
|
||||
## What the inventory shows
|
||||
|
||||
### Summary
|
||||
|
||||
At the top of the variable list, a summary line reports the total variable count and how many fall into each problem category: missing, duplicate, shell-only, unused, and likely secret. Problem counts are highlighted in color so issues stand out at a glance without scrolling through every variable.
|
||||
|
||||
### Status
|
||||
|
||||
For every variable, Sencho records its source, its scope, and a status:
|
||||
|
||||
| Status | Meaning |
|
||||
@@ -32,9 +42,41 @@ For every variable, Sencho records its source, its scope, and a status:
|
||||
| **Duplicate** | Defined in two different places, which can resolve to different effective values. |
|
||||
| **Shell-only** | Referenced and resolved only from the shell of the host, not persisted with the stack, so it will not follow the stack to another node. |
|
||||
|
||||
A variable marked with `${VAR:?message}` in the Compose file is **required**: if it is unset or empty, Compose refuses to start. Required variables carry a separate **required** badge alongside their status badge.
|
||||
|
||||
Variables whose name suggests a secret, such as `DB_PASSWORD`, `API_KEY`, or `CLIENT_SECRET`, are marked with a lock and show presence only. Their value is never read, so it cannot appear in the inventory, the checklist, or anywhere else.
|
||||
|
||||
### Copy env checklist
|
||||
<Frame>
|
||||
<img src="/images/environment-guardrails/env-secrets.png" alt="Variable list showing TUNNEL_TOKEN and VPN_PASS, each with a lock badge labelled 'secret' next to their present status badge." />
|
||||
</Frame>
|
||||
|
||||
The classification is a heuristic based on the variable name. It errs toward marking things as secret. Marking a non-secret as a likely secret only hides a value that the inventory never reads anyway, so there is no downside.
|
||||
|
||||
## Env file status
|
||||
|
||||
The **ENV FILES** section lists every env file the stack declares, with a three-state badge showing whether the file is reachable:
|
||||
|
||||
| Badge | Meaning |
|
||||
|-------|---------|
|
||||
| **present** | The file exists and is readable. |
|
||||
| **missing** | The file is declared in an `env_file:` entry but was not found. Compose will refuse to start the stack until the file is created or the declaration is removed. |
|
||||
| **unverifiable** | The path is absolute or points outside the compose directory. Sencho cannot confirm whether it exists from the hub. The file may work at runtime; use [Compose Doctor](/features/compose-doctor) for a pre-deploy check. |
|
||||
|
||||
Each row also names the services that declare the file.
|
||||
|
||||
## Project environment file
|
||||
|
||||
Compose auto-discovers `.env` in the project directory for `${VAR}` interpolation. When no custom file is configured, the panel shows **Using .env (default)**.
|
||||
|
||||
Users with stack edit permission can replace or supplement this default. An **Add env file** dropdown lists candidates found in the stack directory (`.env`, `*.env`, `.env.*` files). Files are evaluated in the order listed; the first definition of a key wins. Removing all configured files reverts to auto-discovery.
|
||||
|
||||
Any change to the project env file list saves immediately and refreshes the inventory, so the effect on interpolation status is visible at once.
|
||||
|
||||
<Note>
|
||||
The project environment file sub-panel requires the `project-env-files` capability on the active node. Nodes running an older version of Sencho do not have this capability and do not show the sub-panel.
|
||||
</Note>
|
||||
|
||||
## Copy env checklist
|
||||
|
||||
The **copy env checklist** action copies the inventory as a Markdown checklist of variable names, status, and source. It is built for sharing in a ticket or a runbook, so it deliberately contains no values, including for likely secrets.
|
||||
|
||||
@@ -48,6 +90,10 @@ A `${VAR:?message}` reference tells Compose the variable is required: the deploy
|
||||
|
||||
If you would rather fail fast with a clear message before anything runs, turn on **Block deploy on missing required env vars** under **Settings → Infrastructure → Stacks → Deploy Guardrails**. With it on, a deploy or update is refused up front when a required variable is unset or empty, before any backup, image pull, or container change happens. It is off by default, applies to the node you set it on, and requires an admin to change.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/environment-guardrails/settings-deploy-guardrails.png" alt="Settings page at Infrastructure > Stacks showing the Deploy Guardrails section. The 'Block deploy on missing required env vars' toggle is set to OFF." />
|
||||
</Frame>
|
||||
|
||||
## Opening the inventory
|
||||
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
@@ -56,6 +102,29 @@ If you would rather fail fast with a clear message before anything runs, turn on
|
||||
|
||||
The inventory is built against the **active node**, so selecting a remote node inspects the stack on the machine that owns it.
|
||||
|
||||
## Permissions
|
||||
|
||||
Any authenticated user with `stack:read` permission can view the environment inventory, the env file status list, and the summary line.
|
||||
|
||||
Configuring the project environment file list requires `stack:edit` permission. Read-only users see the configured files but cannot add, remove, or reorder them.
|
||||
|
||||
## Connected features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Compose Doctor" icon="stethoscope" href="/features/compose-doctor">
|
||||
Pre-deploy preflight that surfaces missing env files as high-risk findings before any container changes.
|
||||
</Card>
|
||||
<Card title="Stack Dossier" icon="file-lines" href="/features/stack-dossier">
|
||||
The generated facts panel shows the env file path and variable count derived from the same Compose source.
|
||||
</Card>
|
||||
<Card title="Fleet Secrets" icon="lock" href="/features/fleet-secrets">
|
||||
Author encrypted env-var bundles on the hub and push them to labeled stacks across the fleet.
|
||||
</Card>
|
||||
<Card title="Health-Gated Updates" icon="shield-check" href="/features/health-gated-updates">
|
||||
Post-deploy health observation is configured in the same Deploy Guardrails settings section.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
@@ -71,6 +140,12 @@ The inventory is built against the **active node**, so selecting a remote node i
|
||||
<Accordion title="A likely secret is flagged that is not actually sensitive">
|
||||
The classification is a heuristic based on the variable name, and it errs toward marking things as secret. Marking a non-secret as a likely secret only hides a value that the inventory never reads anyway, so there is no downside; the variable still shows its name, source, and status.
|
||||
</Accordion>
|
||||
<Accordion title="An env file shows as unverifiable">
|
||||
The path is absolute or points outside the stack's compose directory, so Sencho cannot check whether it exists from the hub. The file may work fine at runtime. To confirm it is actually present, use the [Compose Doctor](/features/compose-doctor) preflight, which performs an on-node check and will flag the file as missing if it is not found.
|
||||
</Accordion>
|
||||
<Accordion title="The project environment file sub-panel is not visible">
|
||||
The sub-panel requires the `project-env-files` capability on the active node. A node running an older version of Sencho does not have this capability. Update the node to the current version and the sub-panel will appear.
|
||||
</Accordion>
|
||||
<Accordion title="The Environment tab is not there on a remote node">
|
||||
The tab appears when the active node reports that it supports the env inventory. A node running an older version of Sencho does not advertise it, so the tab is hidden for that node until it is updated.
|
||||
</Accordion>
|
||||
|
||||
@@ -24,17 +24,17 @@ The verdict is computed from signals Sencho already tracks, so the check is fast
|
||||
- **Compose Doctor**: the stored result of the last [preflight run](/features/compose-doctor). A blocker finding makes the verdict Blocked; high-risk findings ask for review. If Compose Doctor has never run, the dialog says so without dragging the verdict down.
|
||||
- **Drift**: open [drift findings](/features/stack-drift) warn you that the running state has diverged, so the rollback target may not match what is running.
|
||||
- **Current containers**: a container that is already unhealthy, restarting, or crashed before the update makes the result hard to evaluate; the dialog asks you to look first.
|
||||
- **Pending image change**: the [update preview](/features/auto-update-policies) classifies the pending change. A major version bump asks for review; patch updates and same-tag refreshes pass quietly.
|
||||
- **Healthcheck coverage**: how many services define healthchecks, which determines how thoroughly the post-update health gate can verify the result.
|
||||
- **Pending update**: the [update preview](/features/auto-update-policies) classifies the pending change. A major version bump asks for review; patch updates and same-tag refreshes pass quietly.
|
||||
- **Rollback backup**: whether a backup slot exists and how old it is. A missing backup is only a note, because the update itself creates a fresh one when it starts.
|
||||
- **Node disk**: disk usage near or above the node's alert threshold warns you before a large pull fills the disk.
|
||||
- **Healthcheck coverage**: how many services define healthchecks, which determines how thoroughly the post-update health gate can verify the result.
|
||||
|
||||
Admins also see whether a [fleet snapshot](/features/fleet-backups) covers this stack and can tick **Create a fleet snapshot before updating** to capture one as part of proceeding. If the snapshot fails, the update does not start.
|
||||
|
||||
Every verdict keeps the **Update now** button enabled. The dialog informs the decision; it does not make it for you.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/health-gated-updates/readiness-dialog.png" alt="Update readiness dialog for a stack, showing the verdict chip, the signal rows for Compose Doctor, drift, containers, pending update, rollback backup and node disk, the fleet snapshot row with the create-snapshot checkbox, and the Cancel and Update now buttons" />
|
||||
<img src="/images/health-gated-updates/readiness-dialog.png" alt="Update readiness dialog showing the verdict chip, signal rows for Compose Doctor, Drift, Current containers, Healthcheck coverage, Pending update, Rollback backup and Node disk, the fleet snapshot row with its checkbox, and the Cancel and Update now buttons" />
|
||||
</Frame>
|
||||
|
||||
## The post-update health gate
|
||||
@@ -54,11 +54,11 @@ The deploy progress modal treats the gate verdict as the real result: while the
|
||||
The live verifying and recovery view is part of the deploy progress panel. If you have turned that panel off, the in-browser gate view does not appear, but the gate still runs on the node and records its verdict on the stack timeline.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/health-gated-updates/modal-verifying.png" alt="Deploy progress modal after an update finished, with the status indicator showing Verifying health and the health gate banner reading observing containers" />
|
||||
<img src="/images/health-gated-updates/modal-verifying.png" alt="Deploy progress modal after an update succeeded, showing the Verifying health status and the health gate banner observing containers" />
|
||||
</Frame>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/health-gated-updates/modal-gate-failed.png" alt="Deploy progress modal with the headline status Health gate failed and the banner naming the unhealthy container, with the hint that rollback options are available on the stack" />
|
||||
<img src="/images/health-gated-updates/modal-gate-failed.png" alt="Deploy progress modal with the Health gate failed headline and the banner identifying the unhealthy container, with the hint that rollback options are available on the stack" />
|
||||
</Frame>
|
||||
|
||||
When the gate fails, the stack page surfaces the same [recovery actions](/features/deploy-progress#recovery-actions) as a failed update: retry, restart, roll back when a backup exists, refresh the container state, or copy diagnostics. Rolling back is always your call; the gate never rolls anything back on its own.
|
||||
@@ -73,7 +73,7 @@ Open **Settings > Infrastructure > Stacks > Deploy Guardrails** on the node you
|
||||
- **Observation window** sets how long containers are watched, from 15 to 600 seconds. The default is 90 seconds; raise it for stacks that take a while to settle, since a healthcheck still starting at the end of the window records an unknown verdict instead of a pass.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/health-gated-updates/settings.png" alt="Stacks settings page with the Deploy Guardrails subsection, showing the Observe health after updates toggle and the Observation window field" />
|
||||
<img src="/images/health-gated-updates/settings.png" alt="Stacks settings page showing the Deploy Guardrails subsection, with the Observe health after updates toggle set to On and the Observation window field set to 90 seconds" />
|
||||
</Frame>
|
||||
|
||||
## Rollback readiness
|
||||
@@ -88,7 +88,7 @@ The Stack Dossier carries a **Rollback readiness** section that answers one ques
|
||||
- **Application data**: always reported as not covered. Named volumes and bind-mounted data are not included in file backups; a rollback restores compose and env files only, and your application data keeps its current state. This row exists so the limit is stated where you decide, not discovered during an incident.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/health-gated-updates/dossier-rollback-readiness.png" alt="Rollback readiness section in the Stack Dossier with the overall Ready chip and the six rows: previous compose file, previous env file with variable names only, previous image tag, last successful deploy, healthchecks, and the application data non-coverage disclosure" />
|
||||
<img src="/images/health-gated-updates/dossier-rollback-readiness.png" alt="Rollback readiness section in the Stack Dossier showing the overall state chip and the six rows: Previous compose file, Previous env file, Previous image tag, Last successful deploy, Healthchecks, and the Application data row marked not covered" />
|
||||
</Frame>
|
||||
|
||||
## Classified failures
|
||||
|
||||
@@ -3,159 +3,225 @@ title: Features Overview
|
||||
description: A high-level tour of everything Sencho can do, organized by area.
|
||||
---
|
||||
|
||||
Sencho is a self-hosted cockpit for Docker Compose. The catalog below groups every shipped feature by area so you can scan the surface area at a glance and jump into the deep-dive page for the bits you care about. Tier callouts are inline with each entry; if no tier is mentioned, the feature is available on every Sencho installation.
|
||||
Sencho is a self-hosted cockpit for Docker Compose. The catalog below groups Sencho's features by area, mirroring the Features navigation, so you can scan the surface at a glance and jump into the deep-dive page for the parts you care about. Tier callouts are inline with each entry; if no tier is mentioned, the feature is available on every Sencho installation.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/overview/dashboard-hero.png" alt="Home dashboard showing the system status masthead, the CPU, memory, disk, and network gauge strip with sparklines, and the stack health table sorted by load" />
|
||||
<img src="/images/overview/dashboard-hero.png" alt="Sencho Home dashboard: the Healthy masthead with running container count, fleet-wide CPU, and memory, the CPU sparkline and memory, disk, and network gauge tiles, and the Stack health table sorted by load." />
|
||||
</Frame>
|
||||
|
||||
## Stacks & Deployments
|
||||
## Stacks
|
||||
|
||||
<Frame>
|
||||
<img src="/images/overview/stack-anatomy.png" alt="Stack anatomy view with action buttons, container card, live logs, and a structured panel listing ports, volumes, healthcheck, and network" />
|
||||
<img src="/images/overview/stack-anatomy.png" alt="A stack open in the editor: the action toolbar, the container card with CPU and memory sparklines, the live log stream, and the right-hand panel on the Networking tab showing exposure intent, network memberships, published ports, and a runtime drift check. The full tab strip (Anatomy, Activity, Dossier, Drift, Environment, Networking, Doctor, Storage) is visible across the top of the panel." />
|
||||
</Frame>
|
||||
|
||||
### Stack management
|
||||
|
||||
Deploy, start, stop, restart, update, and remove Docker Compose stacks through a point-and-click interface. Right-click any stack in the sidebar for a context menu with quick actions, including alerts configuration, update checks, and deployment controls. [Learn more →](/features/stack-management)
|
||||
|
||||
### Stack activity
|
||||
|
||||
Every stack has its own event log showing deploys, restarts, starts, stops, and image updates, each event attributed to the user or system that triggered it. The most recent 50 events load immediately and new events stream in live without page refresh; pagination fetches older events on demand. [Learn more →](/features/stack-activity)
|
||||
Deploy, start, stop, restart, update, and remove Docker Compose stacks through a point-and-click interface. Right-click any stack in the sidebar for a context menu that groups actions by purpose: inspect, organize, lifecycle, and destructive operations. [Learn more →](/features/stack-management)
|
||||
|
||||
### Editor
|
||||
|
||||
Full in-browser Monaco editor for `compose.yaml` and `.env` files with syntax highlighting. Toggle edit mode, save to disk, or save and deploy in one step. The container panel shows per-container live stats (CPU, RAM, network), with buttons to open the app, stream logs, or launch a bash terminal. [Learn more →](/features/editor)
|
||||
Full in-browser Monaco editor for `compose.yaml` and `.env` files with syntax highlighting. Toggle edit mode, save to disk, or save and deploy in one step. The container panel shows per-container live stats (CPU, RAM, network), with buttons to open the app, stream logs, or launch a terminal. [Learn more →](/features/editor)
|
||||
|
||||
### Stack file explorer
|
||||
|
||||
Browse, edit, upload, and manage files inside a stack's directory from the dashboard. Read-only browsing and text-file viewing are open to every role; upload, download, edit, folder creation, and file deletion require the admin role. [Learn more →](/features/stack-file-explorer)
|
||||
Browse, preview, and download files inside a stack's directory from the dashboard with any signed-in role. Write actions (upload, edit, rename, move, create folders, change permissions, and delete) require stack edit permission. [Learn more →](/features/stack-file-explorer)
|
||||
|
||||
### Deploy progress
|
||||
### Stack activity
|
||||
|
||||
Stream live output from stack actions in a structured log view with stage badges (PULL, BUILD, CREATE, START, STOP) so you can track each step of the deployment. The modal auto-closes on success and minimizes to a status pill that persists across navigation. [Learn more →](/features/deploy-progress)
|
||||
Every stack has its own event log showing deploys, restarts, starts, stops, image updates, and drift, each event attributed to the user or system that triggered it. The most recent 50 events load immediately and new events stream in live without a page refresh; pagination fetches older events on demand. [Learn more →](/features/stack-activity)
|
||||
|
||||
### Resources hub
|
||||
### Stack Dossier
|
||||
|
||||
View and manage all Docker images, volumes, and networks. Resources are classified as Managed (owned by a Sencho stack), External (part of another Compose project), or Unused / Reclaimable (safe to prune). Run scoped prune operations to clean up Sencho-managed resources only, or target all Docker resources when needed. [Learn more →](/features/resources)
|
||||
The **Dossier** tab in the right-hand Anatomy panel turns each stack into living documentation. Sencho fills in the operational facts it derives from your Compose file, and you add what it cannot infer (purpose, owner, access URLs, network, recovery steps), then export the whole thing as Markdown. [Learn more →](/features/stack-dossier)
|
||||
|
||||
### App Store
|
||||
### Drift Detection
|
||||
|
||||
Browse pre-configured application templates. Filter by category (Media, Automation, Development, etc.), configure environment variables, volumes, and ports, and deploy with a single click. Customize the template source URL to use your own registry. [Learn more →](/features/app-store)
|
||||
The **Drift** tab compares the Compose file on disk against the live Docker runtime and reports exactly where they diverge: a missing service, an undeclared container, a changed image, or different published ports. The check is read-only and keeps a short history of what it has seen. [Learn more →](/features/stack-drift)
|
||||
|
||||
### Atomic deployments
|
||||
### Compose Doctor
|
||||
|
||||
Sencho snapshots your compose and environment files before applying changes. If containers crash after deploy, the previous configuration is restored automatically. [Learn more →](/features/atomic-deployments)
|
||||
The **Doctor** tab runs a preflight check before you deploy. It renders the effective Compose model and reports common failure modes (unset variables, port conflicts, missing bind paths, a mounted Docker socket, a moving `latest` tag) grouped by severity, with a clear fix for each. It is advisory and never blocks a deploy. [Learn more →](/features/compose-doctor)
|
||||
|
||||
### Deploy enforcement
|
||||
### Compose Networking
|
||||
|
||||
Block deploys that violate a scan policy before `docker compose up` runs, with an admin bypass path and a full audit trail. The pre-flight gate enumerates images and rejects deploys when any image meets or exceeds the policy's severity threshold; drift detection continues post-deploy and on schedule. [Learn more →](/features/deploy-enforcement)
|
||||
The **Networking** tab shows how a stack is networked and exposed: its networks, each service's published ports and bindings, and a per-stack or per-service exposure intent (internal, LAN, reverse proxy, public, and more) that Sencho uses to flag when the Compose file disagrees. [Learn more →](/features/compose-networking)
|
||||
|
||||
### Blueprints
|
||||
### Environment & secrets guardrails
|
||||
|
||||
Fleet-wide compose templates that Sencho keeps in sync across the nodes you choose. One declaration covers many nodes via label selectors, drift detection always runs, and stateful blueprints get confirmation prompts before first deploy and before eviction. Admiral. [Learn more →](/features/blueprint-model)
|
||||
The **Environment** tab inventories every variable a stack uses, where each one comes from (Compose interpolation versus container injection), and whether it looks like a secret, all from variable names so no value is ever exposed. It flags missing and duplicate variables and can block a deploy when a required variable has no value. [Learn more →](/features/environment-guardrails)
|
||||
|
||||
### Git sources
|
||||
### Storage portability
|
||||
|
||||
Link a stack to a Git repository and keep one or more compose files in sync via manual pulls or webhook triggers. Merge a base file with environment overrides in order, review a diff before applying changes, create stacks directly from a repo, and optionally sync sibling `.env` files for consistent configuration. [Learn more →](/features/git-sources)
|
||||
The **Storage** tab lists every mount a stack depends on, classifies each one (bind, named, anonymous, tmpfs, or Docker socket) with its read-only flag, and gives the stack a single portability verdict so you know what will break if you move or restore it. [Learn more →](/features/compose-storage)
|
||||
|
||||
### Stack labels
|
||||
|
||||
Tag your stacks with custom colored labels like `production`, `staging`, or `media-server`. Filter the sidebar by label, identify stacks at a glance, and organize your infrastructure visually. Bulk-action a label to deploy, stop, or restart every stack tagged with it. [Learn more →](/features/stack-labels)
|
||||
Tag stacks with custom colored labels like `production`, `staging`, or `media-server`. Filter the sidebar by label, identify stacks at a glance, and bulk-action a label to deploy, stop, or restart every stack that carries it. [Learn more →](/features/stack-labels)
|
||||
|
||||
### Stack sidebar
|
||||
|
||||
Manage, group, and pin your stacks from the primary sidebar, organized by label. Pinned stacks sit in a dedicated `PINNED` group at the top, the context menu groups actions by purpose (Inspect, Organize, Lifecycle, Destructive), and bulk mode (press `B`) acts on a hand-picked subset at once. [Learn more →](/features/sidebar)
|
||||
|
||||
## Deployment
|
||||
|
||||
### Deploy progress
|
||||
|
||||
Stream live output from stack actions in a structured log view with stage badges (PULL, BUILD, CREATE, START, STOP). Choose a modal or an inline band; it auto-dismisses on success and minimizes to a status pill that persists across navigation. [Learn more →](/features/deploy-progress)
|
||||
|
||||
### Atomic deployments
|
||||
|
||||
Sencho snapshots your compose and environment files before applying changes. If containers crash within a few seconds of deploy, the previous configuration is restored automatically. [Learn more →](/features/atomic-deployments)
|
||||
|
||||
### Health-Gated Updates
|
||||
|
||||
Before an update, a readiness dialog gives a single verdict (Ready, Ready with warnings, Review required, Blocked, or Unknown) drawn from Compose Doctor, drift, container health, the pending image change, rollback backup, and node disk. After the update lands, a health gate watches the containers for an observation window and records whether it held. [Learn more →](/features/health-gated-updates)
|
||||
|
||||
### Deploy enforcement
|
||||
|
||||
Block deploys that violate a scan policy before `docker compose up` runs, with an admin bypass path and a full audit trail. The pre-flight gate enumerates images and rejects a deploy when any image meets or exceeds the policy's severity threshold. [Learn more →](/features/deploy-enforcement)
|
||||
|
||||
### Git sources
|
||||
|
||||
Link a stack to a Git repository and keep one or more compose files in sync via manual pulls or webhook triggers. Merge a base file with environment overrides in order, review a diff before applying, create stacks directly from a repo, and optionally sync sibling `.env` files. [Learn more →](/features/git-sources)
|
||||
|
||||
### App Store
|
||||
|
||||
Browse pre-configured application templates. Filter by category (Media, Automation, Development, and more), configure environment variables, volumes, and ports, and deploy with a single click. Point the template source at your own registry when you want. [Learn more →](/features/app-store)
|
||||
|
||||
### Blueprints
|
||||
|
||||
Fleet-wide compose templates that Sencho keeps in sync across the nodes you choose. One declaration covers many nodes via label selectors, drift detection always runs, and stateful blueprints get a confirmation prompt before first deploy and before eviction. Admiral. [Learn more →](/features/blueprint-model)
|
||||
|
||||
## Resources
|
||||
|
||||
### Resources hub
|
||||
|
||||
View and manage all Docker images, volumes, and networks. Resources are classified as Managed (owned by a Sencho stack), External (part of another Compose project), or Unused / Reclaimable (safe to prune). Run scoped prune operations against Sencho-managed resources only, or target all Docker resources when needed. [Learn more →](/features/resources)
|
||||
|
||||
## Observability
|
||||
|
||||
### Dashboard
|
||||
|
||||
The Home view shows real-time system stats at a glance: active containers, exited containers, Docker network activity, and host resource usage (CPU, RAM, disk). Historical CPU and RAM charts display trends over the last 24 hours. A Docker Run converter lets you paste any `docker run` command and convert it to a Compose stack. [Learn more →](/features/dashboard)
|
||||
The Home view shows real-time system stats at a glance: active and exited containers, Docker network activity, and host resource usage (CPU, RAM, disk), with historical CPU and RAM charts over the last 24 hours. A Docker Run converter turns any `docker run` command into a Compose stack. [Learn more →](/features/dashboard)
|
||||
|
||||
### Global search
|
||||
|
||||
Jump to any page, node, or stack from anywhere in the app with `Ctrl+K` (`Cmd+K` on macOS). The palette groups results into Pages, Nodes, and Stacks; cross-node search fans out across every online node; and keyboard navigation keeps your hands on the keys. [Learn more →](/features/global-search)
|
||||
Jump to any page, node, or stack from anywhere with `Ctrl+K` (`Cmd+K` on macOS). The palette groups results into Pages, Nodes, and Stacks, fans out across every online node, and keeps your hands on the keyboard. [Learn more →](/features/global-search)
|
||||
|
||||
### Global observability
|
||||
|
||||
The **Logs** view aggregates output from all containers across all stacks into a single scrollable stream. Filter by stack, log level (stdout/stderr), or search for keywords. Switch to developer mode for real-time SSE streaming instead of polling. [Learn more →](/features/global-observability)
|
||||
The **Logs** view aggregates output from every container across every stack into a single scrollable stream. Filter by stack, log level (stdout/stderr), or keyword, and switch to developer mode for real-time SSE streaming instead of polling. [Learn more →](/features/global-observability)
|
||||
|
||||
### Alerts & notifications
|
||||
|
||||
Configure threshold-based alerts (CPU, memory, network, restart count) per stack. Route notifications to Discord, Slack, or any generic webhook endpoint. Alerts are evaluated every 30 seconds with configurable duration and cooldown. [Learn more →](/features/alerts-notifications)
|
||||
|
||||
### Notification routing
|
||||
|
||||
Route alerts to specific channels with per-stack routing rules. Send production alerts to a critical Slack channel while routing dev stack alerts to a less urgent Discord channel. [Learn more →](/features/alerts-notifications#notification-routing)
|
||||
Configure threshold-based alerts (CPU, memory, network, restart count) per stack and route notifications to Discord, Slack, or any generic webhook. Alerts are evaluated every 30 seconds with configurable duration and cooldown, and per-stack [routing rules](/features/alerts-notifications#notification-routing) send each stack's alerts to the right channel. [Learn more →](/features/alerts-notifications)
|
||||
|
||||
### Audit log
|
||||
|
||||
Track every mutating action across your Sencho instance with a searchable audit trail. See who deployed, stopped, deleted, or changed settings, with timestamps, user attribution, and node context. The recent 14-day window is available on every tier; export, anomaly detection, and extended retention come with Admiral. [Learn more →](/features/audit-log)
|
||||
Track every mutating action across your Sencho instance with a searchable trail: who deployed, stopped, deleted, or changed settings, with timestamps, user attribution, and node context. The recent 14-day window is available on every installation. Admiral adds CSV and JSON export, anomaly detection, and configurable retention. [Learn more →](/features/audit-log)
|
||||
|
||||
## Fleet & Multi-Node
|
||||
## Fleet
|
||||
|
||||
<Frame>
|
||||
<img src="/images/overview/fleet-topology.png" alt="Fleet topology view showing the local node connected by curved links to two remote rack cards, each rendered with a status pill, hostname, CPU, memory, and disk bars" />
|
||||
<img src="/images/overview/fleet-topology.png" alt="Fleet view in grid layout: the fleet masthead showing aggregate CPU, memory, and container counts across all nodes, the tab strip (Overview, Snapshots, Status, Map, Deployments, Routing, Federation, Actions, Secrets), and the node grid with per-node container counts and CPU, RAM, and disk usage bars." />
|
||||
</Frame>
|
||||
|
||||
### Multi-node support
|
||||
|
||||
Add remote Sencho instances as nodes. All dashboard operations (stack management, logs, stats) work identically whether you are targeting your local machine or a server on the other side of the world. Uses a transparent HTTP proxy model; no SSH or shared Docker sockets required. [Learn more →](/features/multi-node)
|
||||
Add remote Sencho instances as nodes. Every dashboard operation (stack management, logs, stats) works identically whether you target your local machine or a server across the world, using a transparent HTTP proxy model with no SSH or shared Docker sockets. [Learn more →](/features/multi-node)
|
||||
|
||||
### Pilot Agent
|
||||
|
||||
Add remote nodes behind NAT, residential networks, or corporate firewalls without exposing any inbound port. The agent runs inside a container on the remote host and holds an outbound WebSocket tunnel to your primary instance; every request rides through the tunnel. [Learn more →](/features/pilot-agent)
|
||||
Add remote nodes behind NAT, residential networks, or corporate firewalls without exposing any inbound port. The agent runs in a container on the remote host and holds an outbound WebSocket tunnel to your primary instance; every request rides through it. [Learn more →](/features/pilot-agent)
|
||||
|
||||
### Sencho Mesh
|
||||
|
||||
Connect containers across nodes by hostname over the Pilot tunnel so multi-node fleets feel like one machine. Opt a stack into the mesh and its services become reachable from any other meshed stack at a stable hostname, with no VPN or firewall changes. Admiral only. [Learn more →](/features/sencho-mesh)
|
||||
Connect containers across nodes by hostname over the Pilot tunnel so a multi-node fleet feels like one machine. Opt a stack into the mesh and its services become reachable from any other meshed stack at a stable hostname, with no VPN or firewall changes. Admiral. [Learn more →](/features/sencho-mesh)
|
||||
|
||||
### Fleet View
|
||||
|
||||
Monitor your entire infrastructure from a single screen. The fleet dashboard shows all nodes with health metrics, container counts, and resource usage. Search, sort, filtering, stack drill-down, the Grid / Topology toggle, and critical-node detection are available on every tier. [Learn more →](/features/fleet-view)
|
||||
Monitor your whole infrastructure from one screen. The Fleet view shows every node with health metrics, container counts, and resource usage in a grid or topology layout, plus a read-only Map of how stacks, services, networks, volumes, and ports relate across the fleet. [Learn more →](/features/fleet-view)
|
||||
|
||||
### Fleet Dossier
|
||||
|
||||
Export your entire homelab as a single Markdown archive. The **Export Dossier** action in the Fleet view walks every node and stack, pairs the facts Sencho derives with the notes you wrote in each [Stack Dossier](/features/stack-dossier), and produces a browsable `homelab-dossier.zip` to commit to Git or store with your backups. Admin-only. [Learn more →](/features/fleet-dossier)
|
||||
|
||||
### Fleet Federation
|
||||
|
||||
Operator-driven placement controls for fleets running Blueprints. Cordon nodes to mark them unschedulable for new work, or pin a blueprint to a specific node to override selector matches. Cordon affects new placements only; existing deployments remain unchanged. Admiral only. [Learn more →](/features/fleet-federation)
|
||||
Operator-driven placement controls for fleets running Blueprints. Cordon a node to mark it unschedulable for new work, or pin a blueprint to a specific node to override selector matches. Cordon affects new placements only; existing deployments keep running. Admiral. [Learn more →](/features/fleet-federation)
|
||||
|
||||
### Fleet Actions
|
||||
|
||||
Run fleet-wide bulk operations from one place: stop stacks across nodes by label selector, propagate a label to stacks across nodes (creating it where missing), or prune Docker resources fleet-wide. Admin-only on every tier. [Learn more →](/features/fleet-actions)
|
||||
Run fleet-wide bulk operations from one tab: stop every stack carrying a label across all nodes, assign a label to selected stacks across nodes (creating it where missing), or prune Docker resources fleet-wide. Admin-only. [Learn more →](/features/fleet-actions)
|
||||
|
||||
### Fleet Sync
|
||||
|
||||
When several Sencho instances run as a fleet, the control instance is the source of truth for security configuration and replicates rules to every remote automatically. Replicas show rules read-only and reject direct write attempts with `403 Forbidden`. [Learn more →](/features/fleet-sync)
|
||||
When several Sencho instances run as a fleet, the control instance is the source of truth for security configuration and replicates scan policies and CVE suppressions to every remote automatically. Replicas show the rules read-only and reject direct writes with `403 Forbidden`. [Learn more →](/features/fleet-sync)
|
||||
|
||||
### Fleet Secrets
|
||||
|
||||
Centralized, encrypted, versioned env-var bundles you can push to labeled nodes' stacks. Bundles are encrypted at rest with AES-256-GCM, every save bumps a version, and every push records a per-node diff in the audit log using overlay merge semantics. Admiral. [Learn more →](/features/fleet-secrets)
|
||||
Centralized, encrypted, versioned env-var bundles you push to labeled nodes' stacks. Bundles are encrypted at rest with AES-256-GCM, every save bumps a version, and every push records a per-node diff in the audit log using overlay merge semantics. Admiral. [Learn more →](/features/fleet-secrets)
|
||||
|
||||
### Fleet-wide backups
|
||||
|
||||
Create point-in-time snapshots of every compose file and environment file across all nodes. Snapshots are stored centrally and can be browsed by node and stack. Restore individual stacks from any snapshot with optional one-click redeploy, even to remote nodes. Both manual and scheduled fleet snapshots are available on every tier. [Learn more →](/features/fleet-backups)
|
||||
Create point-in-time snapshots of every compose and environment file across all nodes, stored centrally and browsable by node and stack. Restore an individual stack from any snapshot with optional one-click redeploy, even to a remote node. Manual and scheduled snapshots are both included. [Learn more →](/features/fleet-backups)
|
||||
|
||||
### Remote updates
|
||||
|
||||
Check for outdated nodes and trigger over-the-air updates from the Fleet View. When the gateway is running a newer version than a remote node, a one-click update pulls the latest image and recreates the container automatically. Both per-node updates and the bulk **Update all** action are available on every tier. [Learn more →](/features/remote-updates)
|
||||
Check for outdated nodes and trigger over-the-air updates from the Fleet view. When the control instance runs a newer version than a remote, a one-click update pulls the latest image and recreates the container; an **Update all** action covers the whole fleet at once. [Learn more →](/features/remote-updates)
|
||||
|
||||
### Node compatibility
|
||||
|
||||
When you manage nodes running different Sencho versions, the dashboard detects each node's capabilities and disables features an older instance does not support, so unsupported actions stay hidden instead of failing. [Learn more →](/features/node-compatibility)
|
||||
|
||||
### Host console
|
||||
|
||||
Open an interactive terminal on the host OS directly in the browser with full xterm.js emulation and color support. No SSH client required; admin-only. [Learn more →](/features/host-console)
|
||||
|
||||
## Automation
|
||||
|
||||
### Scheduled operations
|
||||
|
||||
Automate recurring maintenance tasks like stack restarts, fleet snapshots, system prunes, scans, and image updates on a cron schedule. Every execution is logged with full history so you always know what ran and when. [Learn more →](/features/scheduled-operations)
|
||||
Automate recurring maintenance like stack restarts, fleet snapshots, system prunes, scans, and image updates. Use the simple point-and-click mode for one-time or common recurring schedules, or switch to a full cron expression for precise control. Every run is logged with full history so you always know what ran and when. [Learn more →](/features/scheduled-operations)
|
||||
|
||||
### Auto-Update Policies
|
||||
|
||||
Review pending container updates across your fleet with risk badges (`Safe · patch`, `Review · minor`, `Blocked · major`, `Digest rebuild`) and one-line changelog previews on a single board. The hero counts how many updates are ready to apply without review and surfaces major version bumps as a separate count. [Learn more →](/features/auto-update-policies)
|
||||
|
||||
### Auto-Heal Policies
|
||||
|
||||
Automatically restart containers that fail Docker healthchecks for longer than a threshold. Each policy ships with safety rails: a cooldown period, an hourly restart cap, recent-user-action suppression, and auto-disable on repeated restart failures. [Learn more →](/features/auto-heal-policies)
|
||||
|
||||
### Webhooks
|
||||
|
||||
Trigger stack actions from external CI/CD pipelines over HTTP. Create a webhook targeting a specific stack and action, then call it from GitHub Actions, GitLab CI, or anything that can send a POST. Requests are authenticated with HMAC-SHA256 signatures. [Learn more →](/features/webhooks)
|
||||
|
||||
## Security & Identity
|
||||
|
||||
<Frame>
|
||||
<img src="/images/overview/security-overview.png" alt="The Security page: the 'Action needed' masthead with critical and high CVE counts and last scan time, the tab strip (Overview, Images, Compose risks, Secrets, Policies, Suppressions, History, Scanner setup), the actionable findings summary listing fixable CVEs, detected secrets, and exposed images, and the 30-day risk trend chart." />
|
||||
</Frame>
|
||||
|
||||
### Two-factor authentication
|
||||
|
||||
Protect your Sencho account with a time-based one-time password (TOTP) from an authenticator app and ten single-use backup codes for sign-in recovery. Enroll by scanning a QR code or typing the secret; on every sign-in, Sencho asks for the current six-digit code after your password passes. [Learn more →](/features/two-factor-authentication)
|
||||
Protect your account with a time-based one-time password (TOTP) from an authenticator app and ten single-use backup codes for sign-in recovery. Enroll by scanning a QR code or typing the secret; on every sign-in, Sencho asks for the current six-digit code after your password passes. [Learn more →](/features/two-factor-authentication)
|
||||
|
||||
### RBAC & user management
|
||||
|
||||
Create unlimited accounts with read-only Viewer access to dashboards, logs, and file contents, while keeping deploy and edit permissions locked to admins. Community includes the Admin and Viewer roles; Admiral adds three more (Deployer, Node Admin, Auditor) plus scoped permissions per stack or node. [Learn more →](/features/rbac)
|
||||
Create unlimited accounts with read-only Viewer access to dashboards, logs, and file contents, while keeping deploy and edit locked to admins. Community includes the Admin and Viewer roles; Admiral adds three more (Deployer, Node Admin, Auditor) plus scoped permissions per stack or node. [Learn more →](/features/rbac)
|
||||
|
||||
### SSO & LDAP authentication
|
||||
|
||||
Authenticate with your existing identity provider. Custom OIDC (Authelia, Keycloak, Authentik, any spec-compliant OIDC provider) and preset providers for Google, GitHub, and Okta are available on every tier. Admiral adds LDAP / Active Directory for enterprise directories. SSO works alongside password authentication and auto-provisions accounts on first login with configurable role mapping. [Learn more →](/features/sso)
|
||||
Authenticate with your existing identity provider. Custom OIDC (Authelia, Keycloak, Authentik, any spec-compliant provider) and preset providers for Google, GitHub, and Okta are built in. Admiral adds LDAP / Active Directory. SSO runs alongside password login and auto-provisions accounts on first sign-in with configurable role mapping. [Learn more →](/features/sso)
|
||||
|
||||
### API tokens
|
||||
|
||||
Generate scoped API tokens for CI/CD pipelines, scripts, and automation workflows. Each token is assigned a permission level (Read Only, Deploy Only, or Full Admin) so you can follow the principle of least privilege. [Learn more →](/features/api-tokens)
|
||||
Generate scoped API tokens for CI/CD pipelines, scripts, and automation workflows. Each token carries a permission level (Read Only, Deploy Only, or Full Admin) so you can follow the principle of least privilege. [Learn more →](/features/api-tokens)
|
||||
|
||||
### Security
|
||||
|
||||
A primary surface in the top navigation that brings your security posture into one command center, scoped to the active node. An overview masthead reports your current posture (Action needed, Monitoring, or Secure), and tabs cover image findings, Compose risks, secrets, scan policies, suppressions, scan history, and scanner setup. [Learn more →](/features/security)
|
||||
|
||||
### Vulnerability scanning
|
||||
|
||||
@@ -167,37 +233,13 @@ Accept known-benign vulnerabilities so scan results stay focused on findings tha
|
||||
|
||||
### Private registries
|
||||
|
||||
Store credentials for private Docker registries: Docker Hub organizations, GHCR, and self-hosted registries on every tier, plus AWS ECR on Admiral. Sencho injects them automatically during deploy and pull operations. ECR short-lived tokens are refreshed on every operation. [Learn more →](/features/private-registries)
|
||||
Store credentials for private Docker registries: Docker Hub organizations, GHCR, and self-hosted registries. Admiral adds AWS ECR. Sencho injects them automatically during deploy and pull, refreshing ECR's short-lived tokens on every operation. [Learn more →](/features/private-registries)
|
||||
|
||||
## Automation
|
||||
## Platform & licensing
|
||||
|
||||
### Auto-Update Policies
|
||||
### Appearance
|
||||
|
||||
Review pending container updates across your fleet with risk badges (`Safe · patch`, `Review · minor`, `Blocked · major`, `Digest rebuild`) and one-line changelog previews on a single board. The hero counts pending updates and tells you how many are ready to apply without human review; stacks with a major version bump are surfaced as a separate count for review. [Learn more →](/features/auto-update-policies)
|
||||
|
||||
### Auto-Heal Policies
|
||||
|
||||
Automatically restart containers that fail Docker healthchecks for longer than a specified threshold. Each policy ships with safety rails: a cooldown period, hourly restart cap, recent-user-action suppression, and auto-disable on repeated restart failures. [Learn more →](/features/auto-heal-policies)
|
||||
|
||||
### Webhooks
|
||||
|
||||
Trigger stack actions from external CI/CD pipelines via HTTP webhooks. Create a webhook targeting a specific stack and action, then call it from GitHub Actions, GitLab CI, or any system that can send an HTTP POST. Requests are authenticated with HMAC-SHA256 signatures. [Learn more →](/features/webhooks)
|
||||
|
||||
## Platform
|
||||
|
||||
### Stack sidebar
|
||||
|
||||
Manage, group, and pin your stacks from the primary sidebar, organized by label. Pinned stacks sit in a dedicated `PINNED` group at the top, the context menu groups actions by purpose (Inspect, Organize, Lifecycle, Destructive), and an activity footer shows the most recent stack event. [Learn more →](/features/sidebar)
|
||||
|
||||
### Host console
|
||||
|
||||
Open an interactive terminal on the host OS directly in the browser with full xterm.js emulation and color support. No SSH client required. Admin-only access. [Learn more →](/features/host-console)
|
||||
|
||||
## Reference
|
||||
|
||||
### Node compatibility
|
||||
|
||||
When you manage multiple nodes running different Sencho versions, the dashboard automatically detects each node's capabilities and disables features that are not supported on older instances. No errors, no broken pages. [Learn more →](/features/node-compatibility)
|
||||
Personalize Sencho's look: choose a theme (Dim, OLED, Light, or Auto) and one of eight accent colors, fine-tune contrast, border brightness, and the ambient glow, swap the interface and data fonts, and set the text size. Every choice is saved to the current browser. [Learn more →](/features/appearance)
|
||||
|
||||
### Licensing & billing
|
||||
|
||||
|
||||
@@ -3,93 +3,108 @@ title: Stack Sidebar
|
||||
description: Manage, group, pin, and bulk-act on your stacks from the primary sidebar.
|
||||
---
|
||||
|
||||
The stack sidebar is your cockpit for every stack on the active node. Stacks are grouped by label, your most-used stacks can be pinned to the top, a one-click bulk mode lets you act on several at once, and a live activity footer keeps you aware of what just happened.
|
||||
The stack sidebar is your command center for every stack on the active node. Stacks are grouped by label, frequently used stacks can be pinned to the top, search fans out across all connected remote nodes, bulk mode lets you act on several stacks at once, and an activity ticker at the bottom surfaces ongoing operations and recent events.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-overview.png" alt="Stack sidebar overview with branding header, node switcher, Create Stack button, Bulk mode and Scan icons, search box, ALL/UP/DOWN/UPDATES filter chips, three label groups (MEDIA, UTILITIES, NETWORK) with stack rows and label dots, and a LIVE activity footer at the bottom" />
|
||||
<img src="/images/sidebar/sidebar-overview.png" alt="Stack sidebar showing the Sencho logo with version tag, the NODE LOCAL node switcher, Create Stack button plus bulk mode and scan-folder icons, the Search stacks input, ALL/UP/DOWN/UPDATES filter chips, a starred PINNED group at top, label groups MEDIA and UTILITIES with stack rows each showing a status pill and label dots, and the activity ticker at the bottom reading LIVE VIEW STACK" />
|
||||
</Frame>
|
||||
|
||||
## Layout
|
||||
|
||||
From top to bottom:
|
||||
|
||||
1. **Branding header** shows your Sencho build version.
|
||||
1. **Branding header** shows the Sencho logo and your build version. Hidden on mobile, where the status masthead carries the node context instead.
|
||||
2. **Node switcher** selects which Sencho instance you are managing.
|
||||
3. **Action row** holds **Create Stack**, the **Bulk mode** toggle (square icon), and **Scan stacks folder** (folder-search icon).
|
||||
4. **Search** filters the stack list. Start typing into **Search stacks...** to filter in place.
|
||||
3. **Action row** holds **Create Stack**, the **Bulk mode** toggle (stacked-rows icon), and **Scan stacks folder** (folder-search icon). Only shown when you have permission to create stacks.
|
||||
4. **Search** filters the stack list in real time. Typing into **Search stacks...** also fans out to connected remote nodes (see [Cross-node search](#cross-node-search)).
|
||||
5. **Filter chips** narrow the list by status (described below).
|
||||
6. **Stack list** shows your stacks grouped by label.
|
||||
7. **Activity footer** shows the most recent stack event on the node.
|
||||
6. **Stack list** shows your stacks organized into groups.
|
||||
7. **Activity ticker** shows the highest-priority operation or event signal at any moment.
|
||||
|
||||
## Filter chips
|
||||
|
||||
Four chips sit below the search box. Each shows a live count to the right of its label, and counts above 99 render as **99+** to keep the row from overflowing.
|
||||
|
||||
- **All**: every stack on the node.
|
||||
- **Up**: stacks that are running with nothing crashed. A stack with a cleanly finished one-shot container (an init job that exited without error) still counts as up.
|
||||
- **Up**: stacks that are running with nothing crashed. A stack whose only stopped container finished cleanly (an init job that exited without error) still counts as up.
|
||||
- **Down**: stacks that need attention, whether fully stopped or running with at least one crashed container (the `PT` state described below).
|
||||
- **Updates**: stacks with at least one image update available. The chip turns orange when the count is non-zero so you can spot pending updates at a glance.
|
||||
- **Updates**: stacks with at least one image update available. The chip renders in orange when the count is non-zero so you can spot pending updates at a glance.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-filter-chips.png" alt="Filter chip row with ALL pressed (count 14), UP (count 14), DOWN (count 0), and UPDATES highlighted in orange with count 1" />
|
||||
<img src="/images/sidebar/sidebar-filter-chips.png" alt="Filter chip row showing ALL (15), Up (15), Down (0), and Updates (1) with the Updates chip highlighted in orange and a collapse toggle icon on the right" />
|
||||
</Frame>
|
||||
|
||||
Click the **−** (minus) icon on the right to collapse the chip row when you need more vertical room; it becomes a **+** (plus) icon you click to bring the chips back. The collapsed state is remembered in your browser.
|
||||
Click the **−** icon on the right to collapse the chip row when you need more vertical room; it becomes a **+** icon you click to bring the chips back. The collapsed state is remembered in your browser.
|
||||
|
||||
## Groups
|
||||
|
||||
Stacks are grouped by label. Groups are sorted by size (most populated first), then alphabetically. Stacks with multiple labels appear in every matching group so you always see the full fleet membership per label. Stacks without labels appear in an **Unlabeled** group at the bottom. Click a group header to collapse or expand it; the collapsed state is remembered per node.
|
||||
|
||||
## Pinning
|
||||
## Cross-node search
|
||||
|
||||
Right-click a stack and choose **Pin to top**. Pinned stacks sit in a dedicated **★ PINNED** group at the top of the list. Up to 10 stacks can be pinned per node; pinning an 11th evicts the oldest. Right-click a pinned stack and choose **Unpin** to remove it from the group.
|
||||
When you type anything into **Search stacks...**, Sencho fans the query out to every connected remote node at the same time. Results from other nodes appear below the local list under an **Other nodes** section, separated by a dividing line.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-pinned.png" alt="Sidebar with a starred PINNED group at the top containing plex, sonarr, and cloudflared, followed by the regular MEDIA group below" />
|
||||
<img src="/images/sidebar/sidebar-cross-node-search.png" alt="Sidebar with a search query showing local results in label groups, then a dividing line and an OTHER NODES section listing stacks from three remote nodes with their status pills and external-link arrows" />
|
||||
</Frame>
|
||||
|
||||
Each reachable node shows its matching stacks with a status pill and an external-link arrow. Clicking a remote result switches the active node to that instance and opens the stack immediately.
|
||||
|
||||
If one or more nodes did not respond during the search, a collapsible warning row appears showing how many nodes were unreachable. Expand it to see which nodes failed and why.
|
||||
|
||||
## Pinning
|
||||
|
||||
Right-click a stack and choose **Pin to top**. Pinned stacks sit in a dedicated **★ PINNED** group at the very top of the list. Up to 10 stacks can be pinned per node; pinning an 11th evicts the oldest. Right-click a pinned stack and choose **Unpin** to remove it.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-pinned.png" alt="Sidebar with a starred PINNED group at the top containing plex, followed by the MEDIA group and other label groups below" />
|
||||
</Frame>
|
||||
|
||||
## Stack rows
|
||||
|
||||
Each row gives you everything you need to read the stack at a glance, in a fixed column order:
|
||||
|
||||
- **Status pill** on the left. Two uppercase letters in mono type, or a spinner while a lifecycle action is in flight. `UP` (green) means the stack is running with nothing crashed, `DN` (red) means the stack is stopped, and `PT` (amber) means the stack is partially running: at least one container is up and at least one has crashed (exited with an error, died, or is restart-looping). Hover the `PT` pill to see how many containers are running, such as `3/5 running`. A stack whose only stopped container finished cleanly (an init job that exited without error) stays `UP`.
|
||||
- **Status pill** on the left. Two uppercase letters in mono type, or a spinner while a lifecycle action is in flight. `UP` (green) means the stack is running with nothing crashed. `DN` (red) means the stack is stopped. `PT` (amber) means the stack is partially running: at least one container is up and at least one has crashed (exited with an error, died, or is restart-looping). Hover the `PT` pill to see the running/total container count, such as `3/5 running`. A stack whose only stopped container finished cleanly stays `UP`.
|
||||
- **Stack name** in mono type, truncated with an ellipsis when the row gets tight.
|
||||
- **Label dots** to the right of the name. Up to three colored dots representing the stack's labels render here. If a stack carries more than three labels, a **+N** counter appears for the extras.
|
||||
- **Update indicator**. When a stack has an image update pending, an extra colored dot appears alongside the label dots. If the last registry check could not be determined (registry unreachable, missing credentials, rate limit), a muted "couldn't check" icon shows instead, with the reason on hover. If only a Git source update is pending (no image update), a small Git branch icon shows. Priority is update dot, then check-failed, then Git pending.
|
||||
- **Hover kebab** on the right edge. Hover the row to reveal a vertical three-dot menu that opens the same actions as right-clicking the row.
|
||||
- **Label dots** to the right of the name. Up to three colored dots represent the stack's labels. If a stack carries more than three labels, a **+N** counter appears for the extras.
|
||||
- **Trailing indicator** in its own fixed-width slot at the far right of the name area, showing the highest-priority signal present:
|
||||
- Pulsing orange dot: an image update is available. Hover for the "Update available" tooltip.
|
||||
- Muted circle-alert icon: the last registry check failed (registry unreachable, missing credentials, or rate-limited). Hover to read the failure reason.
|
||||
- Git branch icon: a Git source update is pending and no image update is available.
|
||||
- **Hover kebab** on the right edge. On desktop, hover the row to reveal a vertical three-dot menu that opens the same actions as right-clicking. On touch viewports the kebab is always visible.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-row-anatomy.png" alt="Two stack rows shown stacked: dozzle with two label dots (utilities in grey and media in magenta) and a hover kebab, and cloudflared with an orange indicator dot and a hover kebab" />
|
||||
<img src="/images/sidebar/sidebar-row-anatomy.png" alt="Four stack rows in the MEDIA group: bazarr (UP, purple label dot), plex (UP, purple label dot), radarr (UP, one label dot followed by an orange pulsing update indicator), and seerr (UP, purple label dot)" />
|
||||
</Frame>
|
||||
|
||||
## Bulk mode
|
||||
|
||||
Click the **Bulk mode** icon next to **Create Stack** (or press <kbd>B</kbd>) to enter selection mode. A checkbox appears at the left of every row, and a sticky toolbar slides in just above the list:
|
||||
Click the **Bulk mode** icon next to **Create Stack** (or press <kbd>B</kbd>) to enter selection mode. A checkbox appears at the left of every row, and a sticky toolbar slides in just above the stack list:
|
||||
|
||||
- **Start**, **Stop**, **Restart** apply the action to every selected stack.
|
||||
- **Update** pulls the latest images for every selected stack.
|
||||
|
||||
Click rows to toggle their selection. The toolbar header shows a running count. Click the **×** in the corner of the toolbar to clear the selection, and click the icon again (or press <kbd>B</kbd>) to leave bulk mode.
|
||||
Click rows to toggle their selection. The toolbar header shows a running count. Click the **×** in the toolbar to clear the selection, and click the icon again (or press <kbd>B</kbd>) to leave bulk mode.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-bulk-mode.png" alt="Sidebar in bulk mode with the Bulk mode icon highlighted, a sticky toolbar reading 3 selected with Start, Stop, Restart, and Update buttons, and checkboxes visible on every stack row" />
|
||||
<img src="/images/sidebar/sidebar-bulk-mode.png" alt="Sidebar in bulk mode: the stacked-rows icon in the action row is highlighted in brand color, a sticky toolbar shows 2 selected with a clear X button and Start/Stop/Restart/Update action buttons, and every stack row has a checkbox visible with bazarr and sonarr checked" />
|
||||
</Frame>
|
||||
|
||||
## Context menu
|
||||
|
||||
Right-click any stack (or open the kebab that appears on hover) for its context menu. Items are grouped by purpose:
|
||||
|
||||
- **Inspect**: **Alerts**, **Auto-Heal**, **Check updates**, and **Open App** (the last only appears when the stack is running and exposes a port).
|
||||
- **Organize**: **Labels** (a submenu listing every label on the node, with a check next to each label the stack already carries) and **Pin to top** / **Unpin**.
|
||||
- **Lifecycle**: **Deploy** (when stopped), **Stop**, **Restart**, **Update** (when running), and **Schedule task** (opens the Schedules view pre-filled for this stack, where **Auto-update Stack** sets up unattended updates on your own cadence).
|
||||
- **Destructive**: **Delete**.
|
||||
- **Inspect**: **Alerts**, **Auto-Heal**, **Check updates**, and **Open App** (only appears when the stack is running and exposes a browser-reachable port).
|
||||
- **Organize**: **Labels** opens a submenu listing every label on the node with a checkmark next to each one already assigned to this stack. Click any label to toggle it. The submenu also offers **New label** for inline creation (when you have edit permission and the node is under its label limit) and **Manage labels...** to open the full label settings. Below the Labels entry: **Pin to top** or **Unpin**.
|
||||
- **Lifecycle**: **Deploy** (when stopped), **Stop**, **Restart**, **Update** (when running), and **Schedule task** (admin only, opens the Schedules view with this stack pre-selected).
|
||||
- **Destructive**: **Delete** (only shown when you have permission to delete this stack).
|
||||
|
||||
<Note>
|
||||
Configuring **Auto-Heal** policies (add, toggle, delete) requires an **admin** role. **Schedule task** also requires an **admin** role.
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-context-menu.png" alt="Right-click context menu on a running plex stack with four sections. INSPECT lists Alerts, Auto-Heal, Check updates, and Open App. ORGANIZE lists Labels with a submenu arrow and Pin to top. LIFECYCLE lists Stop, Restart, Update, and Schedule task. DESTRUCTIVE lists Delete in red." />
|
||||
<img src="/images/sidebar/sidebar-context-menu.png" alt="Right-click context menu on a running stack with four labeled sections: INSPECT with Alerts, Auto-Heal, Check updates, and Open App; ORGANIZE with Labels (submenu arrow) and Pin to top; LIFECYCLE with Stop, Restart, Update, and Schedule task; and DESTRUCTIVE with Delete in red" />
|
||||
</Frame>
|
||||
|
||||
## Keyboard shortcuts
|
||||
@@ -118,14 +133,25 @@ On macOS, use <kbd>Cmd</kbd> in place of <kbd>Ctrl</kbd>.
|
||||
| <kbd>P</kbd> | Pin or unpin the stack |
|
||||
| <kbd>B</kbd> | Toggle bulk mode |
|
||||
|
||||
The **↗** and **L ›** glyphs inside the context menu are visual hints, not global bindings. Open **Open App** or **Labels** through the menu (or the hover kebab) to use them.
|
||||
## Activity ticker
|
||||
|
||||
## Activity footer
|
||||
The ticker evaluates a priority cascade on each tick and shows the highest-priority signal available:
|
||||
|
||||
The footer surfaces the most recent stack lifecycle event on the node. Each ticker shows the stack name in brand color, a short event description, and a relative time. The kicker text below alternates between **LIVE · VIEW ACTIVITY →** when an event is showing and **IDLE · NO RECENT ACTIVITY** when nothing has happened recently. A pulsing dot to the left indicates the WebSocket subscription is healthy. Click anywhere on the footer to open the global activity view.
|
||||
| Priority | Condition | Dot | Kicker |
|
||||
|----------|-----------|-----|--------|
|
||||
| 1 | A lifecycle operation is in flight | Brand, pulsing | `LIVE · STREAMING` |
|
||||
| 2 | An unread stack error occurred in the last 24h | Red | `ALERT · VIEW LOGS →` |
|
||||
| 3 | A stack event occurred in the last hour | Brand | `LIVE · VIEW STACK →` |
|
||||
| 4 | An auto-update run is scheduled (and no recent event) | Amber | `AUTOMATION · OPEN SCHEDULE →` |
|
||||
| 5 | The notification WebSocket is reconnecting | Amber | `LIVE · NOTIFICATIONS PAUSED` |
|
||||
| 6 | No recent activity | Green | `LIVE · OPEN ACTIVITY →` |
|
||||
|
||||
During an active operation (priority 1), the ticker shows the action verb ("Deploying", "Restarting", etc.), the stack name in brand color, and the elapsed time. The dot only pulses during an active operation. The ticker is not clickable while an operation is in progress or while notifications are reconnecting.
|
||||
|
||||
Click the ticker in any other state to navigate directly to the linked view.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sidebar/sidebar-activity.png" alt="Activity footer with a green pulsing dot, the stack name 'cloudflared' in brand color, an event description, and the kicker LIVE · VIEW ACTIVITY" />
|
||||
<img src="/images/sidebar/sidebar-activity.png" alt="Activity ticker footer showing a brand-colored dot, a waveform activity icon, a stack name and event description in monospace, and the kicker LIVE VIEW STACK in small uppercase tracking" />
|
||||
</Frame>
|
||||
|
||||
## Troubleshooting
|
||||
@@ -138,10 +164,10 @@ The footer surfaces the most recent stack lifecycle event on the node. Each tick
|
||||
Each node has a 10-stack pin limit. When you pin an 11th stack, the oldest pin is automatically evicted so the new one fits. Unpin a stack you no longer need before adding another, or accept that the oldest will roll off.
|
||||
</Accordion>
|
||||
<Accordion title="Keyboard shortcuts are not firing">
|
||||
Shortcuts only fire when a stack row is selected in the sidebar (one row is highlighted). They are intentionally blocked while a text input is focused or the global command palette (<kbd>Ctrl</kbd>+<kbd>K</kbd>) is open, so they do not collide with typing. Click a stack row, then try the shortcut again.
|
||||
Shortcuts only fire when a stack row is selected in the sidebar (one row is highlighted). They are blocked while a text input is focused or the global command palette (<kbd>Ctrl</kbd>+<kbd>K</kbd>) is open. Click a stack row, then try the shortcut again.
|
||||
</Accordion>
|
||||
<Accordion title="The filter chips disappeared from the sidebar">
|
||||
Click the **+** icon to the right of the search box to bring them back. The chip row collapses to a thin **−** / **+** toggle, and the state is remembered in your browser, so an earlier collapse persists across reloads until you expand it again.
|
||||
Click the **+** icon to the right of the search box to bring them back. The chip row collapses to a **−** / **+** toggle, and the state is remembered in your browser, so an earlier collapse persists across reloads until you expand it again.
|
||||
</Accordion>
|
||||
<Accordion title="Schedule task is missing from the right-click menu">
|
||||
Scheduling requires an **admin** role. Ask an admin on this node to schedule the task for you, or sign in with an admin account.
|
||||
@@ -149,4 +175,7 @@ The footer surfaces the most recent stack lifecycle event on the node. Each tick
|
||||
<Accordion title="Auto-Heal panel opens but Add Policy and the toggles are missing">
|
||||
Reading existing Auto-Heal policies is open to every operator, so the panel still opens for non-admins. Adding, enabling, disabling, or deleting policies requires the **admin** role. Sign in with an admin account, or ask an admin on this node to make the change.
|
||||
</Accordion>
|
||||
<Accordion title="Cross-node search shows some nodes as unreachable">
|
||||
Sencho tried to reach those nodes during the search but they did not respond. The node may be offline, its connection credentials may have expired, or a network change may have interrupted the link. Open the Fleet view to check each node's status, or go to Settings and test the connection from the Nodes panel.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -21,14 +21,14 @@ SSO is available on every Sencho tier. Custom OIDC and the preset providers for
|
||||
|
||||
### LDAP flow
|
||||
|
||||
1. User enters their directory username and password on the Sencho login page.
|
||||
1. User selects **LDAP** on the **Local / LDAP** toggle beside the **Sign in** heading and enters their directory username and password.
|
||||
2. Sencho binds to LDAP with a service account, locates the user, then verifies their password.
|
||||
3. On the user's first login, a Sencho account is created automatically.
|
||||
4. Sencho issues a session JWT and the user is logged in, identical to a password login.
|
||||
|
||||
### OIDC / OAuth flow (Google, GitHub, Okta, Custom OIDC)
|
||||
|
||||
1. User clicks the provider button on the login page (e.g., **Sign in with Google**).
|
||||
1. User clicks the provider button on the login page (the provider name under an **Or continue with** divider, e.g., **Google**).
|
||||
2. The browser is redirected to the identity provider for authentication.
|
||||
3. After granting consent, the provider redirects back to Sencho with an authorization code.
|
||||
4. Sencho exchanges the code for tokens, verifies the ID token, and reads user information.
|
||||
@@ -72,12 +72,12 @@ If the user's ID token contains a `groups` claim with the value `sencho-admins`,
|
||||
|
||||
SSO can be configured two ways:
|
||||
|
||||
1. **Settings UI**: go to **Settings → SSO** in the Sencho dashboard. Enable providers, paste credentials, and test connections from the UI. Changes take effect immediately, no restart required.
|
||||
1. **Settings UI**: go to **Settings → Access → SSO** in the Sencho dashboard. Enable providers, paste credentials, and test connections from the UI. Changes take effect immediately, no restart required.
|
||||
2. **Environment variables**: set `SSO_*` variables in your Docker Compose file. They seed the database on first boot; afterwards the Settings UI is authoritative.
|
||||
|
||||
### Via Settings UI
|
||||
|
||||
Admins manage SSO providers in **Settings → SSO**. The page lists every provider as a collapsible card with a label, an **enable / disable** toggle pill on the right, and an **Active** badge on the header when the provider is on.
|
||||
Admins manage SSO providers in **Settings → Access → SSO** (admin only; hidden on remote nodes). The masthead shows the SCOPE (global), the number of configured **PROVIDERS**, and how many are **ENABLED**. The page lists every provider as a collapsible card with a label, an **enable / disable** toggle pill on the right, and an **Active** badge on the header when the provider is on.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sso/sso-settings.png" alt="SSO settings panel listing the five identity providers as collapsible cards with enable / disable toggles" />
|
||||
@@ -309,7 +309,7 @@ If not set, Sencho auto-detects the URL from the request's `Host` header and pro
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="SSO buttons do not appear on the login page">
|
||||
Verify the provider is **enabled** (toggle on the Active state) in **Settings → SSO** and that the configuration saved successfully. The login page fetches the list of enabled providers when it loads; hard-refresh the tab if changes were just made.
|
||||
Verify the provider is **enabled** (toggle on, showing the **Active** badge) in **Settings → Access → SSO** and that the configuration saved successfully. The login page fetches the list of enabled providers when it loads; hard-refresh the tab if changes were just made.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -317,4 +317,4 @@ The [operations troubleshooting page](/operations/troubleshooting#ldap-connectio
|
||||
|
||||
## Combining SSO with two-factor authentication
|
||||
|
||||
SSO and [two-factor authentication](/features/two-factor-authentication) work together. By default, SSO sign-ins skip the TOTP challenge, since the identity provider has already authenticated the user. Operators who want a stricter posture can flip **Require 2FA on SSO sign-in** on their **Settings → Account & Security** card to require both factors on every SSO sign-in. The toggle only appears once at least one SSO provider is enabled.
|
||||
SSO and [two-factor authentication](/features/two-factor-authentication) work together. By default, SSO sign-ins skip the TOTP challenge, since the identity provider has already authenticated the user. Operators who want a stricter posture can turn on **Require 2FA on SSO sign-in** in **Settings → Personal → Account** to require both factors on every SSO sign-in. The toggle sits in the Two-factor authentication section and appears once you have 2FA enrolled and at least one SSO provider is enabled.
|
||||
|
||||
@@ -3,101 +3,123 @@ title: Stack Activity
|
||||
description: Per-stack event log showing deploys, restarts, config changes, and alerts, each attributed to the user or system process that triggered them.
|
||||
---
|
||||
|
||||
The **Activity** tab in the right-hand **Anatomy** panel gives you a timestamped record of everything that happened to the selected stack: deploys, restarts, starts, stops, and image updates. Each entry shows who triggered the event so you always have operational context. The `files` and `edit` actions on the same strip belong to the panel as a whole and stay available regardless of which tab is active.
|
||||
The **Activity** tab in the right-hand **Anatomy** panel gives you a timestamped record of everything that happened to the selected stack: deploys, restarts, stops, starts, image updates, drift events, and health gate outcomes. Every entry is attributed to the user or background process that triggered it, so you always have full operational context at a glance. The **files** and **edit** buttons on the same header strip belong to the panel as a whole and stay available regardless of which tab is active.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-activity/activity-tab-populated.png" alt="Stack Activity tab showing events grouped by Today, Yesterday, and Earlier with restart, auto-update, and image-update entries" />
|
||||
<img src="/images/stack-activity/activity-tab-populated.png" alt="Activity tab with the ACTIVITY tab selected in the header strip showing ANATOMY, ACTIVITY, DOSSIER, DRIFT, ENVIRONMENT, NETWORKING, DOCTOR, STORAGE tabs. Under a TODAY header, three events appear: 'plex deployed by admin' with a rocket icon 52m ago, 'plex updated by admin' with an up-arrow icon 12h ago, and 'plex update started by admin' with an up-arrow icon 12h ago." />
|
||||
</Frame>
|
||||
|
||||
## What you'll see
|
||||
|
||||
Each entry in the activity list contains:
|
||||
Each entry in the activity list contains four pieces of information:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Icon** | Category of the event (deploy, restart, stop, start, image update applied) |
|
||||
| **Message** | What happened (for example, `dozzle restarted`). Sensitive values such as tokens, secrets, basic-auth credentials, and bearer tokens are redacted before storage |
|
||||
| **Attributed to** | `by <username>` for user-initiated actions; `via <Subsystem>` for events generated by background processes (Auto-Heal, Scheduler, Image Update, Docker, Blueprint, Monitor, Policy) |
|
||||
| **Icon** | Category of the event (deploy, restart, stop, start, image update, drift, and others) |
|
||||
| **Message** | What happened, for example `plex deployed`. Sensitive values such as tokens, secrets, basic-auth credentials, and bearer tokens are redacted before storage |
|
||||
| **Attribution** | `by <username>` for user-initiated actions; `via <Subsystem>` for events triggered by background processes such as Auto-Heal or the Scheduler |
|
||||
| **Time** | Relative time since the event: `just now`, `5m ago`, `2h ago`, `3d ago` |
|
||||
|
||||
## Event categories
|
||||
|
||||
The five operational categories below have a dedicated icon. Other notification categories that scope to a stack (deploy failures, available image updates, auto-heal triggers, monitor alerts, scan findings) also flow into the timeline and render with a generic activity icon.
|
||||
Ten categories have a dedicated icon. Any other notification scoped to a stack (deploy failures, scan findings, monitor alerts) also flows into the timeline and renders with a generic activity icon.
|
||||
|
||||
| Category | Icon | Example message |
|
||||
|----------|------|-----------------|
|
||||
| Deploy succeeded | rocket | `plex deployed` |
|
||||
| Stack restarted | refresh | `dozzle restarted` |
|
||||
| Stack stopped | stop | `radarr stopped` |
|
||||
| Stack started | play | `radarr started` |
|
||||
| Image update applied | up arrow | `Auto-update: stack "dozzle" updated with new images` |
|
||||
| Category | Example message |
|
||||
|----------|-----------------|
|
||||
| Deploy succeeded | `plex deployed` |
|
||||
| Stack restarted | `dozzle restarted` |
|
||||
| Stack stopped | `radarr stopped` |
|
||||
| Stack started | `radarr started` |
|
||||
| Image update applied | `Auto-update: stack "dozzle" updated with new images` |
|
||||
| Drift detected | `Drift detected on sonarr: 2 findings` |
|
||||
| Drift resolved | `Drift resolved on tautulli: 1 finding cleared` |
|
||||
| Update started | `tautulli update started` |
|
||||
| Health gate passed | `plex: health gate passed` |
|
||||
| Health gate failed | `plex: health gate failed` |
|
||||
|
||||
## Day grouping
|
||||
|
||||
Events are grouped by day under three headers: **Today**, **Yesterday**, and **Earlier**. Within each group, events are ordered most-recent first. The grouping recomputes every minute so a panel left open across midnight rolls the **Today** bucket into **Yesterday** automatically.
|
||||
Events are grouped by day under three headers: **Today**, **Yesterday**, and **Earlier**. Within each group, events appear most-recent first. The grouping recomputes every minute so a panel left open past midnight automatically rolls the **Today** bucket into **Yesterday**.
|
||||
|
||||
## Live updates
|
||||
|
||||
New events stream in over the same WebSocket that powers the notification bell, so the list updates without a page refresh while the tab is open. Duplicate events are deduplicated by ID and the timeline is sorted by `(timestamp, id)` so events emitted within the same millisecond stay in a stable order.
|
||||
New events stream in over the same WebSocket that powers the notification bell. The list updates without a page refresh while the tab is open. Duplicate events are deduplicated by ID, and the timeline is sorted by `(timestamp, id)` so events emitted within the same millisecond stay in a stable order.
|
||||
|
||||
If the WebSocket connection drops, a banner appears at the top of the timeline:
|
||||
|
||||
> Live updates offline; reconnecting…
|
||||
|
||||
New events are paused until the connection re-establishes. Existing entries remain visible. Once the banner clears, new events resume automatically without any action on your part.
|
||||
|
||||
## Pagination
|
||||
|
||||
The tab loads the most recent 50 events on open. If a stack has more recorded events, a **Load more** button appears at the bottom of the list. Each click fetches the next 50 older events. The button stops appearing once the timeline reaches the end of the stored history.
|
||||
The tab loads the 50 most recent events on open. If a stack has more recorded events, a **Load more** button appears at the bottom of the list. Each click fetches the next 50 older events. The button disappears once the timeline reaches the end of the stored history.
|
||||
|
||||
The underlying API endpoint, `GET /api/stacks/{stackName}/activity`, accepts an optional `before` (timestamp) and `beforeId` (row id) pair for cursor-based pagination. The two parameters must move together; sending `beforeId` without `before` is rejected.
|
||||
The underlying API endpoint, `GET /api/stacks/{stackName}/activity`, accepts an optional `before` (timestamp) and `beforeId` (row id) pair for cursor-based pagination. Both parameters must be sent together; sending `beforeId` without `before` is rejected.
|
||||
|
||||
## Empty state vs unavailable
|
||||
|
||||
Two empty-looking states exist and they mean different things:
|
||||
Two states can make the tab look empty, and they mean different things:
|
||||
|
||||
- **No activity recorded yet** appears when the stack genuinely has no recorded events.
|
||||
- **Activity unavailable** appears when the request to fetch events fails (for example, the node became unreachable mid-load). A **Retry** button is shown next to it.
|
||||
- **No activity recorded yet** appears when the stack genuinely has no recorded events. This is normal for stacks that have not had any lifecycle actions since Sencho began tracking activity.
|
||||
- **Activity unavailable** appears when the request to fetch events fails (for example, the node became unreachable mid-load). A **Retry** button is shown next to this message.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-activity/activity-tab-empty.png" alt="Stack Activity tab in its empty state with a heartbeat icon and the message 'No activity recorded yet'" />
|
||||
<img src="/images/stack-activity/activity-tab-empty.png" alt="Activity tab showing a small activity icon and the message 'No activity recorded yet' centered in the panel, with no events listed." />
|
||||
</Frame>
|
||||
|
||||
## Who triggered an event
|
||||
|
||||
Sencho attributes every recorded event to whichever actor performed it:
|
||||
Sencho attributes every recorded event to the actor that performed it:
|
||||
|
||||
- **User-initiated actions** (deploy, restart, stop, start, image update from a button click or the API): the actor is the authenticated username, shown as `by <username>`.
|
||||
- **Background subsystems** (Auto-Heal, Scheduler, Image Update, Docker event monitoring, Blueprint reconciler, Host Monitor, Policy enforcement): the actor is the subsystem name, shown as `via Auto-Heal`, `via Scheduler`, and so on.
|
||||
- **User-initiated actions** (deploy, restart, stop, start, image update triggered from a button or the API): the actor is the authenticated username, shown as `by <username>`.
|
||||
- **Background subsystems**: the actor is the subsystem name, shown as `via <Subsystem>`.
|
||||
|
||||
An autoheal restart and a manual restart are visually distinct in the timeline because the actor differs.
|
||||
| Background subsystem | Displayed as |
|
||||
|----------------------|--------------|
|
||||
| Generic system event | `via System` |
|
||||
| Auto-Heal | `via Auto-Heal` |
|
||||
| Scheduler | `via Scheduler` |
|
||||
| Image Update | `via Image Update` |
|
||||
| Docker event monitor | `via Docker` |
|
||||
| Blueprint reconciler | `via Blueprint` |
|
||||
| Host Monitor | `via Monitor` |
|
||||
| Policy enforcement | `via Policy` |
|
||||
|
||||
An Auto-Heal restart and a manual restart produce separate entries in the timeline because their actors differ, even when the event message is otherwise identical.
|
||||
|
||||
## Retention
|
||||
|
||||
Activity is stored in a per-instance SQLite table. Two limits apply, whichever comes first:
|
||||
Activity is stored in a per-instance SQLite table. Two limits apply, whichever is reached first:
|
||||
|
||||
- **Time-based**: events older than the configured retention window (30 days by default) are removed during the next periodic cleanup.
|
||||
- **Per-stack cap**: the most recent 500 events per (node, stack) are retained. Older events for that stack are removed during the next periodic cleanup.
|
||||
- **Time-based:** events older than the configured retention window (30 days by default) are removed during the next periodic cleanup.
|
||||
- **Per-stack cap:** the most recent 500 events per (node, stack) are kept. Older events for that stack are pruned on the next cleanup run.
|
||||
|
||||
Events without an associated stack (system-level notifications) are kept up to 1000 per node before the oldest are removed.
|
||||
Events without an associated stack (system-level notifications) are kept up to 1,000 per node before the oldest are removed.
|
||||
|
||||
Cleanup runs on the same schedule as container-metrics cleanup, so retention applies even on long-running instances.
|
||||
|
||||
## Accessing the Activity tab
|
||||
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
2. Switch to the **Activity** tab in the Anatomy panel header.
|
||||
2. Select the **Activity** tab in the Anatomy panel header.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The Activity tab shows 'No activity recorded yet'">
|
||||
The activity log is populated by operations performed through Sencho on this stack: deploys, restarts, starts, stops, and image updates. Trigger one of those actions and the entry appears within a few seconds.
|
||||
This is normal for stacks that have not had any lifecycle actions (deploy, restart, stop, update) since Sencho started tracking activity. The tab populates the first time a lifecycle event is recorded. Events that occurred before Sencho was installed are not backfilled.
|
||||
</Accordion>
|
||||
<Accordion title="The Activity tab shows 'Activity unavailable'">
|
||||
The request to fetch activity failed, usually because the node is currently unreachable or the backend returned a server error. Click **Retry** to attempt the request again. If the node has been removed or its credentials are stale, the activity for that node is no longer accessible.
|
||||
The request to fetch activity failed, usually because the node is unreachable or the backend returned a server error. Click **Retry** to re-fire the request without reloading the page. If the node has been removed or its credentials have changed, go to **Settings → Nodes** to verify the connection.
|
||||
</Accordion>
|
||||
<Accordion title="An event I expected to see is missing">
|
||||
Two reasons this can happen. First, the event may be older than the 30-day retention window. Second, if the stack has had more than 500 events recorded, the oldest are pruned automatically. To preserve a longer history, route the events to an external channel via **Notification Routes**.
|
||||
<Accordion title="An event I triggered is not appearing in the timeline">
|
||||
Live events arrive over WebSocket. If the "Live updates offline; reconnecting…" banner is visible, new events are paused until the connection recovers. Scroll down to check whether the event is already present below the visible viewport, or wait for the banner to clear and the event will appear automatically. If the event still does not show up, check the notification bell for error-level alerts that may indicate the action itself did not complete.
|
||||
</Accordion>
|
||||
<Accordion title="The same restart shows as both 'by <username>' and 'via Auto-Heal'">
|
||||
These are two distinct events. The first is your manual click; the second is Auto-Heal restarting the same container after a separate unhealthy-duration breach. Each event is its own row with its own actor.
|
||||
These are two distinct events. The first is a manual restart; the second is Auto-Heal restarting the container after a separate unhealthy-duration breach. Each event is its own entry with its own actor. Two rows here are correct behavior, not a duplicate.
|
||||
</Accordion>
|
||||
<Accordion title="Operational events don't appear in the bell notification dropdown">
|
||||
The notification dropdown surfaces system alerts and error-level events. User-initiated operational events (start, stop, restart, deploy, update) appear exclusively in the per-stack Activity tab, keeping the global tray focused on conditions that need your attention.
|
||||
<Accordion title="Operational events don't appear in the notification bell dropdown">
|
||||
The notification bell surfaces system alerts and error-level events. User-initiated operational events (start, stop, restart, deploy, update) appear exclusively in the per-stack Activity tab, keeping the global tray focused on conditions that need your attention.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -1,87 +1,162 @@
|
||||
---
|
||||
title: Stack Dossier
|
||||
description: Turn each Compose stack into living documentation by pairing the facts Sencho already derives with operator notes it cannot infer, then export the whole thing as Markdown.
|
||||
description: Pair the operational facts Sencho derives from each stack with your own notes, then export everything as a portable Markdown document.
|
||||
---
|
||||
|
||||
The **Dossier** tab in the right-hand **Anatomy** panel turns a stack into living documentation. Sencho fills in the operational facts it already knows from your Compose file, and you add the context it cannot infer: what the stack is for, who owns it, where it lives on your network, and how to recover it. The `files` and `edit` actions on the same strip belong to the panel as a whole and stay available regardless of which tab is active.
|
||||
The **Dossier** tab in the Anatomy panel is where you turn a running stack into something a teammate, a future self, or a runbook can actually use. It pairs facts Sencho already has (services, ports, volumes, env files, restart policy) with the context only you know: what the stack does, who owns it, where it sits on the network, and how to recover it if the host is lost. The result is a single document that stays current on the generated side and grows richer as you fill in the operator side.
|
||||
|
||||
The goal is to stop scattering this information across spreadsheets, `.env` comments, and memory. Sencho is already close to the source of truth, so it keeps the generated half in sync while you maintain the rest.
|
||||
This stops operational knowledge from living in spreadsheets, comments, and people's heads while Sencho is already reading the Compose file.
|
||||
|
||||
## How it works
|
||||
|
||||
The tab has two halves. The top half is read-only: Sencho derives facts from the Compose file on every load. The bottom half is a form you fill in. Neither half depends on the other; a stack with no operator notes still has accurate generated facts, and a stack you have not deployed yet still accepts operator notes.
|
||||
|
||||
The export (copy or download) combines both halves into one Markdown document. The document format is designed to be readable outside Sencho: in a Git repo, a wiki, an Obsidian vault, or alongside a backup. Sencho never embeds secrets into the export; only variable names and counts appear from the generated side, and your operator notes are exported as you write them.
|
||||
|
||||
## Generated facts
|
||||
|
||||
The top of the tab shows a read-only summary derived live from the stack's current Compose anatomy. It is never copied into a field you have to maintain, so it can never drift:
|
||||
The top of the tab shows a read-only summary derived live from the stack's Compose file. Because it is never copied into a field you maintain, it cannot drift independently of the file.
|
||||
|
||||
| Fact | Source |
|
||||
|------|--------|
|
||||
| **Services** | Service names declared in the Compose file |
|
||||
| **Ports** | Published host ports across all services |
|
||||
| **Volumes** | Mounted volumes count |
|
||||
| **Network** | The stack's network |
|
||||
| **Restart** | The restart policy |
|
||||
| **Env file** | The env file, its variable count, and any referenced `${VAR}` with no value |
|
||||
| **Source** | Git source when the stack is linked, otherwise local |
|
||||
<Frame>
|
||||
<img src="/images/stack-dossier/dossier-overview.png" alt="Dossier tab showing the Generated Facts panel above and the Operator Notes form below" />
|
||||
</Frame>
|
||||
|
||||
Environment and label values are never read or shown: only variable **names** and **counts** appear, exactly as in the Anatomy tab. The one exception is a secret interpolated into a structural fact, such as a published port written as `${DB_PORT}`, which Compose resolves before the facts are read, so its value appears. The same is true of the resolved paths and network names shown on the Storage and Networking tabs. Keep secrets in `environment:` or `env_file:` rather than interpolating them into structural fields. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
| Fact | What is shown | Source |
|
||||
|------|--------------|--------|
|
||||
| **Services** | Names of declared services | Compose file service keys |
|
||||
| **Ports** | Count and all published host ports across all services | `ports:` entries, host-side bindings only |
|
||||
| **Volumes** | Count of mounted volumes | `volumes:` references across services |
|
||||
| **Network** | Stack's primary network name and type (for example, `arr-net · bridge`) | Compose network definitions |
|
||||
| **Restart** | Restart policy of the first service with one set | `restart:` key |
|
||||
| **Env file** | The env file path, its variable count, and names of any `${VAR}` with no supplied value | `.env` / `env_file:` at the stack root |
|
||||
| **Source** | `local`, or the Git remote URL, branch, and path when the stack is linked | Stack's git source configuration |
|
||||
|
||||
**Multi-file Compose stacks:** when a stack has a Git source with multiple Compose files, Sencho merges them (equivalent to `docker compose config`) before reading facts. Services, ports, and volumes shown in the Generated Facts reflect the merged result, not any individual file.
|
||||
|
||||
Environment and label values are never read or shown: only variable names and counts appear, exactly as in the Anatomy tab. The one exception is a value interpolated into a structural field such as `${DB_PORT}` in a port declaration, which Compose resolves before the facts are read. Keep secrets in `environment:` or `env_file:` rather than interpolating them into structural fields. See [Environment and Secrets Guardrails](/features/environment-guardrails).
|
||||
|
||||
## Operator notes
|
||||
|
||||
Below the generated facts is a form for the details Sencho cannot derive. Every field is optional:
|
||||
Below the generated facts is a form for the details Sencho cannot derive. Every field is optional. Save applies all fields at once; clearing a field and saving removes it. The Save button activates only when there are unsaved changes.
|
||||
|
||||
| Field | What to record |
|
||||
|-------|----------------|
|
||||
| **Purpose** | What this stack is for |
|
||||
| **Owner** | Who maintains it |
|
||||
| **Access URLs** | Where it is reached (one URL per line) |
|
||||
| **Static IP** | The stack's static or LAN IP |
|
||||
| **VLAN** | The VLAN it sits on |
|
||||
| **Firewall** | Ports opened, rules, zones |
|
||||
| **Reverse proxy** | Hostnames, upstreams, TLS notes |
|
||||
| **Backup** | What to back up and how |
|
||||
| **Upgrade** | Upgrade steps and gotchas |
|
||||
| **Recovery** | How to rebuild the stack from scratch |
|
||||
| **Notes** | Anything else worth recording |
|
||||
|
||||
Notes are saved per stack and per node, so the same stack name on two different nodes keeps its own dossier. Anyone can read a dossier; saving changes requires stack edit permission, so read-only users see the notes but not a Save button.
|
||||
| Field | Max length | What to record |
|
||||
|-------|-----------|---------------|
|
||||
| **Purpose** | 1,000 chars | One or two sentences: what the stack does and why it runs |
|
||||
| **Owner** | 1,000 chars | Team, person, or alias responsible for this stack |
|
||||
| **Static IP** | 255 chars | The host IP or LAN address this stack is pinned to, if any |
|
||||
| **VLAN** | 255 chars | VLAN tag or name if the stack is on a segmented network |
|
||||
| **Access URLs** | 2,000 chars | Full URLs where the stack is reachable, one per line. Include the port explicitly (`http://homelab:8096`) rather than relying on scheme defaults, so the documentation drift check can validate the port against what the stack publishes. |
|
||||
| **Firewall** | 8,000 chars | Ports opened in firewall rules, zone assignments, any ingress or egress rules |
|
||||
| **Reverse proxy** | 8,000 chars | Upstream name and port, virtual host, TLS termination, cert source |
|
||||
| **Backup** | 8,000 chars | Which volumes or bind-mounts matter, backup tool, schedule, offsite policy |
|
||||
| **Upgrade** | 8,000 chars | Steps required to update this stack, any ordering dependencies, known breaking changes between versions |
|
||||
| **Recovery** | 8,000 chars | Steps to rebuild this stack from scratch if the host is lost. Write this as if no prior state exists. |
|
||||
| **Notes** | 8,000 chars | Anything else: quirks, known bugs, related stacks, useful references |
|
||||
|
||||
## Documentation drift
|
||||
|
||||
Documentation should describe reality, so Sencho watches the one place it most often slips: a port you wrote into **Access URLs** that the stack no longer publishes. When Compose moves a service from `:32400` to `:32401`, or an access URL points at a port nothing serves, a warning appears in the Dossier tab naming the port to review.
|
||||
Documentation should describe reality, so Sencho watches the one place it most often slips: a port you wrote into **Access URLs** that the stack no longer publishes. When Compose moves a service from `:32400` to `:32401`, or an access URL points at a port nothing serves, a warning appears in the Dossier tab naming the port to review. This is why the Access URLs field asks for full URLs with explicit ports.
|
||||
|
||||
The check is deterministic and advisory. It reads the ports in your Access URLs and compares them against the published ports in the generated facts above, the same ports the Anatomy tab shows. It never edits your notes or your Compose file, and it never guesses: a URL with no explicit port, or on the scheme default (`:80` for `http`, `:443` for `https`), is left alone, since those usually reach the stack through a reverse proxy rather than a published port. When a stack publishes a port through a variable (such as `${PLEX_PORT}:32400`), the real value is unknown, so the check stays quiet rather than risk a false warning. A bare single-label host with a port (such as `plex:32400`) is also left alone, since it cannot be told apart from a plain note; write it as a full URL (`http://plex:32400`) to have its port checked. Fix either side, the access URL or the stack's ports, and the warning clears on its own.
|
||||
<Frame>
|
||||
<img src="/images/stack-dossier/dossier-doc-drift.png" alt="Dossier tab showing a documentation drift warning for an access URL whose port does not match any published port" />
|
||||
</Frame>
|
||||
|
||||
The check is deterministic and advisory. It reads the ports in your Access URLs and compares them against the published ports in the generated facts above, the same ports the Anatomy tab shows. It never edits your notes or your Compose file, and it never guesses:
|
||||
|
||||
- A URL with no explicit port, or on the scheme default (`:80` for `http`, `:443` for `https`), is left alone. Those usually reach the stack through a reverse proxy rather than a published port.
|
||||
- When a stack publishes a port through a variable such as `${PLEX_PORT}:32400`, Sencho does not know the resolved value from the Compose source, so the check stays quiet rather than risk a false warning.
|
||||
- A bare single-label host with a port (such as `plex:32400`) is also left alone, since it cannot be distinguished from a plain note. Write it as a full URL (`http://plex:32400`) to have its port checked.
|
||||
|
||||
Fix either side (the access URL or the stack's ports) and the warning clears on its own.
|
||||
|
||||
## Markdown export
|
||||
|
||||
Two actions in the tab header produce a single Markdown document combining the generated facts and your notes:
|
||||
Two actions in the tab header produce a single Markdown document combining the generated facts and your operator notes:
|
||||
|
||||
- **copy md** copies it to the clipboard.
|
||||
- **download** saves it as `<stack>-dossier.md`.
|
||||
<Frame>
|
||||
<img src="/images/stack-dossier/dossier-export-bar.png" alt="Dossier tab header showing the COPY MD and DOWNLOAD export buttons" />
|
||||
</Frame>
|
||||
|
||||
- **copy md** copies the document to the clipboard.
|
||||
- **download** saves it as `<stack-name>-dossier.md`.
|
||||
|
||||
The exported document always includes the Generated Facts and all Operator Notes. When the Networking tab capability is available on the active node, the export also includes a network exposure summary. When the Storage tab capability is available, the export also includes a storage summary. Both additions are automatic when the capability is present.
|
||||
|
||||
The export is clean Markdown that reads well in Git, Obsidian, BookStack, a README, or stored alongside a backup. The generated facts never include `.env` values, only variable names and counts. Your operator notes are exported exactly as you write them, so treat them like any document you might share and avoid pasting secrets you do not want in the export.
|
||||
|
||||
The export buttons are disabled when the Compose file cannot be parsed. Your operator notes are unaffected and still save normally even when parsing fails.
|
||||
|
||||
To export every stack across every node at once, use the [Fleet Dossier](/features/fleet-dossier) action in the Fleet view.
|
||||
|
||||
## Connected features
|
||||
|
||||
### Drift tab (compose vs runtime)
|
||||
|
||||
Adjacent to the Dossier tab in the same Anatomy panel is the **Drift** tab. It answers a different question: does what is actually running still match the Compose file? Drift detection compares services, images, and published ports against the live Docker runtime. It is independent of the dossier: you do not need operator notes for drift detection, and documentation drift warnings in the Dossier tab are separate from runtime drift in the Drift tab. See [Drift Detection](/features/stack-drift).
|
||||
|
||||
### Fleet Dossier
|
||||
|
||||
The [Fleet Dossier](/features/fleet-dossier) export in the Fleet view produces a ZIP archive containing Markdown documents for every stack on every node, organized by node. It uses the same per-stack dossier format and pulls the operator notes you have saved in each stack's Dossier tab.
|
||||
|
||||
### Fleet Snapshots and dossier notes
|
||||
|
||||
Fleet Snapshots can optionally capture dossier notes alongside Compose files. When the **snapshot documentation** setting is enabled, each snapshot includes the operator notes for every stack that has them. When restoring a snapshot, you can choose whether to restore files only, or files and dossier notes together. Dossier restore is opt-in at restore time and does not overwrite notes unless you confirm it. See [Fleet Backups](/features/fleet-backups).
|
||||
|
||||
### Activity timeline
|
||||
|
||||
Events from the Drift tab (drift detected, drift resolved) appear in the stack's **Activity** timeline alongside deploys and restarts, giving a chronological view of when the stack drifted and when it recovered.
|
||||
|
||||
## Permissions and per-node scoping
|
||||
|
||||
Any authenticated user with `stack:read` permission can view a stack's dossier. Read-only users see all fields and the export buttons, but no Save button appears.
|
||||
|
||||
Saving changes requires stack edit permission (`stack:edit`). In multi-user setups with role-based access control, check your role if the Save button is absent.
|
||||
|
||||
Dossiers are stored per stack and per node. A stack named `plex` on your home node and `plex` on a remote VPS have independent dossiers. Edit a stack's dossier while that node is the active node to update the right one.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Operator notes are per-instance.** Notes entered on one Sencho instance are not synchronized to another instance that manages the same stack. Fleet Snapshots with documentation capture is the only way to transfer notes between instances.
|
||||
- **Documentation drift checks Access URLs only.** The port-validity check applies only to the Access URLs field. It does not validate other fields or check hostnames.
|
||||
- **No history on operator notes.** The dossier stores only the current version of each field. Sencho does not record change history for operator notes. Use a version-controlled wiki or Git if you need note history.
|
||||
- **Export requires a parseable Compose file.** When the Compose file cannot be parsed, the export buttons are disabled. Operator notes are unaffected and still save normally.
|
||||
- **Rollback readiness requires a specific capability.** The rollback readiness section is shown only when the `update-guard` capability is active for the node.
|
||||
|
||||
## Accessing the Dossier tab
|
||||
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
2. Switch to the **Dossier** tab in the Anatomy panel header.
|
||||
3. Fill in any fields you want and click **Save**. An **unsaved changes** marker appears while edits are pending.
|
||||
|
||||
The Anatomy panel is visible in the right column when a stack is open and the editor is not active (that is, when not in the compose-file edit mode). If the panel is hidden, click away from the edit view or close the editor using the `X` button at the top of the editor card.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The Save button stays greyed out">
|
||||
Save is enabled only when there are unsaved changes. Edit any field and it activates. If it never activates, you may be signed in as a read-only user: editing a dossier requires stack edit permission, while reading it does not.
|
||||
<Accordion title="The Save button is not visible or stays greyed out">
|
||||
The Save button appears only for users with stack edit permission. If you do not see it, you are signed in as a read-only user or your role does not include `stack:edit` for this stack. Users with read-only access can view all fields and use the export buttons.
|
||||
|
||||
If the button is visible but greyed out, it activates only when there are unsaved changes. Edit any field and it enables.
|
||||
</Accordion>
|
||||
<Accordion title="The generated facts say 'Unable to parse compose.yaml'">
|
||||
The summary is built from the stack's Compose file. If the file is empty or not valid YAML, the facts cannot be generated and the export buttons are disabled. Fix the Compose file in the editor and the facts reappear. Your operator notes are unaffected and still save.
|
||||
<Accordion title="Generated facts show 'Unable to parse compose.yaml'">
|
||||
The facts panel is built from the stack's Compose file. If the file is empty, missing, or not valid YAML, the facts cannot be generated. Fix the Compose file in the editor and the panel updates. Your operator notes are unaffected and save normally even when the compose file is unparseable. The export buttons are also disabled when the file cannot be parsed.
|
||||
</Accordion>
|
||||
<Accordion title="My notes for the same stack differ between two nodes">
|
||||
That is expected. A dossier is stored per stack and per node, so a stack with the same name on two nodes has an independent dossier on each. Edit the dossier while that node is the active node to update the right one.
|
||||
<Accordion title="My notes for the same stack name differ between two nodes">
|
||||
That is expected. A dossier is stored per stack and per node, so a stack with the same name on two nodes keeps an independent dossier on each. Make sure the correct node is active before saving, or use Fleet Snapshots with documentation capture to copy notes between instances.
|
||||
</Accordion>
|
||||
<Accordion title="An access URL spilled onto one line in the export">
|
||||
The Access URLs field treats each line as a separate URL. Put one URL per line in the form and they are preserved as separate lines in the exported Markdown.
|
||||
The Access URLs field treats each line as one URL. If a URL wrapped visually in the form, it was entered on a single line. Paste each URL on its own line to have them appear as separate entries in the export.
|
||||
</Accordion>
|
||||
<Accordion title="A documentation drift warning flags a port my reverse proxy fronts">
|
||||
The warning means an access URL names a port no service in the stack publishes. That is expected when a reverse proxy serves the stack on a port the stack itself does not publish. The warning is advisory and changes nothing: either record the reverse-proxied URL without the internal port, or publish the port in Compose if the URL should reach it directly.
|
||||
The warning appears when an access URL names a port the stack does not publish. That is expected when a reverse proxy answers on a port the stack never published. The warning is advisory and changes nothing: either write the reverse-proxied URL without an explicit port (so it is not checked), or publish the port in the Compose file if the URL should reach the stack directly.
|
||||
</Accordion>
|
||||
<Accordion title="A documentation drift warning appears for a port defined as a variable">
|
||||
When a stack's compose file publishes a port through a variable such as `${PLEX_PORT}:32400`, Sencho does not know the resolved value from the Compose source, so it treats that port as unverifiable and stays quiet. If your access URL refers to the same port and a warning still appears, the URL's port does not match any statically declared published port. Either update the URL or pin the port in the Compose file.
|
||||
</Accordion>
|
||||
<Accordion title="The networking or storage summary is missing from the export">
|
||||
The Markdown export includes a network exposure summary and a storage summary only when the corresponding capabilities are available on the active node. If those sections are missing, the capabilities are not enabled for that node. The core generated facts and all operator notes are always included regardless of capabilities.
|
||||
</Accordion>
|
||||
<Accordion title="Dossier notes were not restored after a fleet snapshot restore">
|
||||
Fleet snapshot restore has two steps: restoring Compose files and optionally restoring dossier notes. Notes are restored only when you explicitly choose to restore them at restore time. If you did not select that option, or if the original snapshot was captured without documentation enabled, the notes will not be present. To restore notes, choose a snapshot that includes documentation and confirm the notes restore option during the restore flow.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -3,78 +3,185 @@ title: Drift Detection
|
||||
description: See at a glance whether a stack's running containers still match the Compose file on disk, with specific, actionable reasons when they have diverged.
|
||||
---
|
||||
|
||||
The **Drift** tab in the right-hand **Anatomy** panel answers a single day-two question: does what is actually running still match the Compose file on disk? Sencho treats your Compose file as the source of truth, so it compares the file against the live Docker runtime and reports exactly where the two have diverged.
|
||||
The **Drift** tab answers the core day-two operations question: does what is actually running still match the Compose file on disk?
|
||||
|
||||
The check is read-only. It tells you what changed and never alters a stack on its own, so you can trust the report before deciding what to do about it. Opening the tab builds the comparison fresh. When you deploy a stack through Sencho, it also records the Compose file it deployed as a baseline, so it can later tell you whether the file has changed since then, and it keeps a short history of the findings it has seen.
|
||||
Sencho treats your Compose file as the source of truth. Every time you open the Drift tab, it compares the live Docker runtime against the file and tells you exactly where the two differ. If you have deployed through Sencho, it also compares the current file against the baseline it recorded at deploy time, so you know whether the file has already changed for the next deploy.
|
||||
|
||||
The check is always read-only. It reports drift; it never fixes it on its own.
|
||||
|
||||
<img
|
||||
src="/images/stack-drift/drift-in-sync.png"
|
||||
alt="The Drift tab showing In sync status with Matches last deploy below it and a resolved network finding in the history"
|
||||
/>
|
||||
|
||||
## How it works
|
||||
|
||||
Drift detection is split into two layers that work together.
|
||||
|
||||
**The spatial engine** performs a stateless diff between the Compose file on disk and the containers currently running in Docker, then returns a report. It never writes anything to the database, so opening the tab is always side-effect-free.
|
||||
|
||||
**The drift ledger** sits on top of the spatial engine and provides persistence. When you click re-check, or when Sencho automatically reconciles after a deploy, it records new findings with timestamps, marks resolved findings as cleared, and writes events to the stack's Activity timeline. This turns a point-in-time snapshot into a short history of when drift appeared and when it cleared.
|
||||
|
||||
## Status
|
||||
|
||||
Every stack resolves to one of four states, shown as a badge at the top of the tab:
|
||||
The status badge at the top of the tab summarizes the comparison between the Compose file and the running containers.
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| **In sync** | The running containers match the Compose file: same services, images, and published ports. |
|
||||
| **Drifted** | Something running differs from the file. The specific reasons are listed below the badge. |
|
||||
| **Not running** | The stack is defined on disk but no containers are running. |
|
||||
| **Unreachable** | Docker could not be reached, so drift cannot be assessed right now. |
|
||||
| **In sync** | Running containers match the Compose file: same services, images, and published ports. |
|
||||
| **Drifted** | Something running differs from the file. Specific findings are listed below the badge. |
|
||||
| **Not running** | The stack has no running containers. The file is present but nothing is up. |
|
||||
| **Unreachable** | Docker could not be reached on the active node, so drift cannot be assessed. |
|
||||
|
||||
## Since your last deploy
|
||||
|
||||
Below the status badge, a second line compares the Compose file on disk against the version you last deployed through Sencho:
|
||||
Below the status badge, a second signal compares the Compose file on disk against the version that was deployed through Sencho most recently.
|
||||
|
||||
<img
|
||||
src="/images/stack-drift/drift-drifted.png"
|
||||
alt="The Drift tab showing Drifted status with Source changed below it, and an image finding in the Findings section showing expected vs running image"
|
||||
/>
|
||||
|
||||
| Signal | Meaning |
|
||||
|--------|---------|
|
||||
| **Matches last deploy** | The Compose file is unchanged since you last deployed it. |
|
||||
| **Source changed** | The Compose file has been edited since the last deploy. If the change affects the model (an image, port, or service), Sencho says so; a comments or formatting only edit is called out separately. |
|
||||
| **No deploy baseline** | This stack has not been deployed through Sencho yet, so there is nothing to compare against. Deploy it once to start tracking. |
|
||||
| **Matches last deploy** | The Compose file is unchanged since the last deploy. |
|
||||
| **Source changed** | The Compose file has changed since the last deploy. If the change is structural (an image, port, or service definition), Sencho says so. A formatting-only or comment-only edit is noted separately. |
|
||||
| **No deploy baseline** | This stack has not yet been deployed through Sencho, so there is no recorded baseline. Deploy it once to start tracking. |
|
||||
|
||||
This is independent of the runtime status above: a stack can be **In sync** with its running containers while its file has already **changed** for the next deploy.
|
||||
This signal is independent of the runtime status. A stack can be **In sync** with its running containers while its file has **already changed** for the next deploy, or it can be **drifted** at runtime while the file itself is unchanged from what was deployed.
|
||||
|
||||
<Note>
|
||||
Sencho records two hashes at deploy time: a raw-file hash (catches any text change, including formatting) and a parsed-model hash (only changes when structure changes, ignoring comments and whitespace). The "source changed" signal uses both: if only the raw hash differs, the edit was formatting or comments. If the model hash also differs, a structural change has been made.
|
||||
</Note>
|
||||
|
||||
## Findings
|
||||
|
||||
When a stack is drifted, each reason is listed against the service it affects:
|
||||
When a stack is drifted, each reason is listed in the **Findings** section against the service it affects.
|
||||
|
||||
| Finding | What it means |
|
||||
|---------|---------------|
|
||||
| **Service missing** | The Compose file declares a service, but it has no running container. |
|
||||
| **Undeclared** | A container is running for the stack, but no matching service exists in the Compose file. |
|
||||
| **Image** | A running container uses a different image than the Compose file declares. The expected and running values are shown side by side. |
|
||||
| **Image** | A running container uses a different image than the Compose file declares. Expected and actual values are shown side by side. |
|
||||
| **Ports** | The published ports of a service differ from what the Compose file declares. |
|
||||
| **Service missing** | The Compose file declares a service, but no running container matches it. |
|
||||
| **Undeclared service** | A container is running for the stack but no matching service exists in the Compose file. |
|
||||
| **Network undeclared** | A running service is attached to a network that is not declared in the Compose file. |
|
||||
| **Network missing** | A network declared in the Compose file is not used by any running service or is absent from the runtime. |
|
||||
|
||||
Image references are compared after normalizing the implicit Docker Hub registry and a missing tag to `:latest`, so `nginx` and `docker.io/library/nginx:latest` are treated as the same image. A running container pinned to a digest is compared against the declared tag as written.
|
||||
### Image comparison
|
||||
|
||||
Image references are normalized before comparison, so equivalent forms do not produce false positives. `nginx`, `docker.io/nginx`, and `docker.io/library/nginx:latest` all resolve to the same reference.
|
||||
|
||||
If a service has multiple replicas, any replica running a different image than the declared one triggers an image finding. This catches stacks that are mid-update with mixed versions running simultaneously.
|
||||
|
||||
If the Compose file declares a tag (`:latest`, `:1.25`) and the running container was pulled from a digest pin, the two forms are compared as-is. A digest-pinned container reads as an image mismatch against a tag declaration. This is intentional: the two references are not equivalent.
|
||||
|
||||
### Port ranges
|
||||
|
||||
A Compose port range such as `8000-8002:8000-8002` is compared conservatively. Because the runtime expands ranges into individual ports, the comparison can read as a difference even when the deployment is correct. Sencho errs toward surfacing the possible difference rather than hiding it.
|
||||
|
||||
## When drift is recorded
|
||||
|
||||
**Opening the tab** runs the spatial engine and displays the current state. It does not write anything to the ledger or update the history.
|
||||
|
||||
**Clicking re-check** runs the spatial engine and then reconciles the result into the ledger: new findings are recorded with a detection timestamp, cleared findings are marked resolved, and Activity timeline events are written for any change. The history and "checked" timestamp update after a re-check.
|
||||
|
||||
**After every deploy or update**, Sencho automatically records a new baseline hash and runs a full reconciliation. You do not need to click re-check after deploying; the ledger is updated as part of the deploy pipeline.
|
||||
|
||||
<img
|
||||
src="/images/stack-drift/drift-history.png"
|
||||
alt="The Drift tab showing both the Findings section and the Drift history section with an open finding marked just now"
|
||||
/>
|
||||
|
||||
## Drift history
|
||||
|
||||
Each time you **re-check** a stack, and after every deploy, Sencho records the findings it sees. The **Drift history** list under the findings shows recent entries with when each was first **detected** and, once it clears, when it was **resolved**. This turns a point-in-time check into a short ledger of how a stack has drifted and recovered over time.
|
||||
The **Drift history** section shows up to the 20 most recent findings, both open and resolved. Open findings appear first.
|
||||
|
||||
The history is labelled with when it was last checked. The status badge at the top is always live, recomputed each time you open the tab, while the history reflects the last time the ledger was reconciled. When the two differ, re-check to bring the history up to date.
|
||||
Each entry shows:
|
||||
- The service name and finding type (image, ports, service, or network)
|
||||
- A description of the specific difference
|
||||
- When the finding was first detected
|
||||
- When it was resolved, if it has cleared
|
||||
|
||||
Drift that appears or clears is also written to the stack's **Activity** timeline, so **Drift detected** and **Drift resolved** events sit alongside deploys and restarts.
|
||||
The history header shows when the ledger was last reconciled ("checked X ago"). The status badge at the top always reflects the current runtime state, computed fresh each time the tab is loaded. If the two disagree, a re-check will bring the history up to date.
|
||||
|
||||
### Activity timeline
|
||||
|
||||
Drift events also appear in the stack's **Activity** tab alongside deploys, restarts, and updates:
|
||||
|
||||
- **Drift detected**: recorded when a re-check or post-deploy reconciliation finds new open findings
|
||||
- **Drift resolved**: recorded when findings that were previously open have all cleared
|
||||
|
||||
## Accessing the Drift tab
|
||||
|
||||
1. Click any stack in the left sidebar to open it.
|
||||
2. Switch to the **Drift** tab in the Anatomy panel header.
|
||||
3. Read the status badge and any findings. Use **re-check** to run the comparison again after you deploy or change something.
|
||||
<img
|
||||
src="/images/stack-drift/drift-tab-location.png"
|
||||
alt="The tab bar at the top of the Anatomy panel showing Anatomy, Activity, Dossier, Drift, Environment, Networking, Doctor, and Files tabs with Drift selected"
|
||||
/>
|
||||
|
||||
On a phone, the same report appears under the **Compose** section of the stack detail.
|
||||
1. Click any stack in the sidebar to open it.
|
||||
2. In the right-hand Anatomy panel, switch to the **Drift** tab.
|
||||
3. Read the status badge and any findings.
|
||||
4. Click **re-check** after editing the Compose file or after a manual Docker operation to refresh the ledger.
|
||||
|
||||
On mobile, the same report appears under the **Compose** section of the stack detail view.
|
||||
|
||||
## Requirements
|
||||
|
||||
Drift detection is available on all plans with no additional configuration. As long as the stack has a Compose file and Docker is reachable on the node, the tab is functional.
|
||||
|
||||
## Limitations
|
||||
|
||||
**Read-only and advisory.** Drift detection never alters a stack. It reports differences and records them in the ledger, but deploying to resolve drift is a separate, explicit action.
|
||||
|
||||
**No automatic background scanning.** Drift is checked when you open the tab, when you click re-check, and after every deploy or update through Sencho. There is no automatic background poll that checks stacks on a schedule outside of those events.
|
||||
|
||||
**History cap.** The ledger keeps the 20 most recent findings per stack. Older entries are not shown in the UI.
|
||||
|
||||
**Port ranges.** Compose port ranges are compared conservatively and may report a difference even when the deployment is correct.
|
||||
|
||||
**Unreachable Docker.** When Docker cannot be reached, the status shows as Unreachable and no findings are reported. Open findings in the ledger are not resolved just because the check could not run; they remain open until a successful check confirms they have cleared.
|
||||
|
||||
**Parse errors.** If the Compose file cannot be parsed, no findings are extracted from it and the ledger is not updated. Resolve the parse error and re-check.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="A stack I deliberately stopped shows as 'Not running'">
|
||||
That is expected. Drift compares the file on disk against what is actually running, so a stack with no running containers reports **Not running** even when you stopped it on purpose. Deploy it to return it to **In sync**.
|
||||
That is expected. Drift compares the file on disk against what is actually running, so a stack with no running containers reports **Not running** even when you stopped it intentionally. Deploy it to return it to **In sync**.
|
||||
</Accordion>
|
||||
<Accordion title="The image finding flags a stack I just updated">
|
||||
Open **re-check** after the deploy finishes. During a rolling update, replicas can briefly run different images, and the report is a snapshot of that moment. Once every container is on the declared image, the finding clears.
|
||||
Click **re-check** after the deploy finishes. During a rolling update, replicas can briefly run different images, and the report captures that moment. Once every container is on the declared image, the finding clears.
|
||||
</Accordion>
|
||||
<Accordion title="A stack that uses a published port range shows a ports finding">
|
||||
A Compose port range such as `8000-8002:8000-8002` is compared conservatively and can read as a ports difference even when the deployment is correct. Sencho errs toward surfacing a possible difference rather than hiding one. The status reflects this as drift you can confirm against the file.
|
||||
A Compose port range such as `8000-8002:8000-8002` is compared conservatively and can read as a ports difference even when the deployment is correct. Sencho errs toward surfacing a possible difference rather than hiding one.
|
||||
</Accordion>
|
||||
<Accordion title="The status says 'Unreachable'">
|
||||
Sencho could not reach Docker on the active node, so it cannot compare the runtime. Confirm the Docker engine is running and the node is online, then use **re-check**. Other stacks on the same node will show the same state until Docker responds. While Docker is unreachable, Sencho does not change the recorded drift history, so an open finding is never cleared just because the check could not run.
|
||||
Sencho could not reach Docker on the active node, so it cannot compare the runtime. Confirm the Docker engine is running and the node is online, then use **re-check**. Other stacks on the same node will show the same state until Docker responds. While Docker is unreachable, Sencho does not update the drift history, so open findings are never cleared just because the check could not run.
|
||||
</Accordion>
|
||||
<Accordion title="The tab says 'No deploy baseline'">
|
||||
Sencho records a baseline the first time you deploy a stack through it. A stack you imported or have not yet deployed from Sencho has nothing to compare against. Deploy it once from Sencho and the line changes to **Matches last deploy**.
|
||||
Sencho records a baseline the first time you deploy a stack through it. A stack that was imported or has not yet been deployed from Sencho has nothing to compare against. Deploy it once from Sencho and the signal changes to **Matches last deploy**.
|
||||
</Accordion>
|
||||
<Accordion title="The Activity tab shows a drift event but the Drift tab now shows In sync">
|
||||
The Activity event was written when drift was detected or resolved during a previous reconciliation. The Drift tab always shows the current live state, which may already be back in sync. If the history and badge disagree, click **re-check** to reconcile the ledger with the current runtime.
|
||||
</Accordion>
|
||||
<Accordion title="Opening the tab does not update the history">
|
||||
Opening the tab runs the spatial engine (read-only) and updates the status badge, but does not write to the ledger. Only **re-check** and post-deploy reconciliation persist findings and update the history. Click re-check explicitly when you want the history to reflect the current state.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Stack Activity" icon="clock-rotate-left" href="/features/stack-activity">
|
||||
Drift detected and resolved events appear in the Activity timeline alongside deploys and restarts.
|
||||
</Card>
|
||||
<Card title="Stack Dossier" icon="book-open" href="/features/stack-dossier">
|
||||
The Dossier tab stores documentation and access URLs; its documentation drift feature checks whether URLs still match the stack's published ports.
|
||||
</Card>
|
||||
<Card title="Atomic Deployments" icon="rocket" href="/features/atomic-deployments">
|
||||
Understanding how Sencho deploys stacks explains when and how drift baselines are recorded.
|
||||
</Card>
|
||||
<Card title="Blueprint Model" icon="drafting-compass" href="/features/blueprint-model">
|
||||
Blueprints have a separate policy-based drift mode (observe, suggest, enforce) distinct from the runtime drift detection described on this page.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -197,7 +197,7 @@ Each item is processed independently. If some succeed and others fail (for examp
|
||||
Right-click any file and choose **Permissions** to inspect or edit its Unix mode bits. The dialog shows a 3 by 3 grid of `r` / `w` / `x` toggles for Owner, Group, and Other, plus the current octal value.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-file-explorer/permissions-dialog.png" alt="Permissions modal for a file showing the rwx grid for Owner, Group, and Other plus the octal value 644" />
|
||||
<img src="/images/stack-file-explorer/permissions-dialog.png" alt="Permissions modal for a file showing the rwx grid for Owner, Group, and Other plus the current octal value" />
|
||||
</Frame>
|
||||
|
||||
When your account has stack edit permission, the toggles are interactive and the footer adds **Save**. For viewer accounts the dialog opens read-only: the toggles render the current state and the footer shows only **Close**.
|
||||
@@ -227,11 +227,11 @@ When the entry is one of the five protected names, the modal asks you to type th
|
||||
## Context menu reference
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-file-explorer/context-menu-folder.png" alt="Right-click menu on a folder showing New File, New Folder, Rename, and Delete entries" />
|
||||
<img src="/images/stack-file-explorer/context-menu-folder.png" alt="Right-click menu on a folder showing New File, New Folder, Rename, Duplicate, Copy to, Move to, and Delete entries" />
|
||||
</Frame>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-file-explorer/context-menu-file.png" alt="Right-click menu on a file showing Rename, Permissions, and Delete entries" />
|
||||
<img src="/images/stack-file-explorer/context-menu-file.png" alt="Right-click menu on a file showing Rename, Duplicate, Copy to, Move to, Permissions, and Delete entries" />
|
||||
</Frame>
|
||||
|
||||
Right-click anywhere on a row, not only on its name, to open the menu.
|
||||
|
||||
@@ -3,18 +3,19 @@ title: "Stack Labels"
|
||||
description: "Per-node tags that group your stacks by purpose, surface them under collapsible headers in the sidebar, and unlock cross-stack bulk actions across the fleet."
|
||||
---
|
||||
|
||||
A **Stack Label** is a per-node tag (name plus color) you can stick on any stack. Once a stack carries a label, the sidebar groups it under that label's header instead of dumping every stack into a flat list, and Fleet View can filter the overview by tag. Admins also get a pair of fleet-wide actions powered by labels: stop every stack labeled `prod` across every node, or add a label to stacks across nodes in one shot.
|
||||
A **Stack Label** is a per-node tag (name plus color) applied to any stack. A labeled stack groups under that label's header in the sidebar instead of the flat list, and Fleet View can filter the overview by tag. Admins also get two label-driven fleet actions: stop every stack labeled `prod` across every node, or add a label to stacks across nodes in one shot.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/sidebar-grouping.png" alt="Sidebar showing stacks grouped under three uppercase label headers (MEDIA 6, UTILITIES 5, NETWORK 3) and an UNLABELED 1 group at the bottom. Each stack row carries a small colored dot on the trailing edge that matches the group's color." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/sidebar-grouping.png"
|
||||
alt="Sidebar showing stacks grouped under four uppercase label headers (MEDIA 7, UTILITIES 4, NETWORK 3, DATABASE 1). Each group shows its stacks with a small colored dot on the trailing edge of each row."
|
||||
/>
|
||||
|
||||
## What problem this solves
|
||||
|
||||
A flat sidebar of fifteen stacks all rendering at the same level forces you to scan every name to find the one you want. With Stack Labels:
|
||||
A flat sidebar of fifteen same-level stacks forces a scan of every name. With Stack Labels:
|
||||
|
||||
- **Stacks group by purpose, not by alphabet.** Each label becomes a collapsible section header, sorted by stack count, so the busy buckets (your media stack, your network stack) sit at the top and rarely-touched ones can be folded away.
|
||||
- **A glance is enough.** Each row also carries up to three colored dots on its trailing edge, so a stack tagged with two purposes (for example `prod` and `media`) shows both colors without you having to open the assignment menu.
|
||||
- **A glance is enough.** Each row carries up to three colored dots on its trailing edge, so a stack tagged with two purposes (for example `prod` and `media`) shows both colors.
|
||||
- **Bulk operations stop being copy-paste.** Stop every `prod` stack across the fleet from one card. Re-tag eight stacks at once when a service moves between concerns. No scripting, no per-stack menu hunt.
|
||||
|
||||
## Anatomy of a label
|
||||
@@ -24,13 +25,13 @@ A flat sidebar of fifteen stacks all rendering at the same level forces you to s
|
||||
| **Name** | 1 to 30 characters. Letters, digits, spaces, and hyphens only (`^[a-zA-Z0-9 -]+$`). Case-sensitive and unique per node. |
|
||||
| **Color** | One of ten swatches: teal, blue, purple, rose, amber, green, orange, pink, cyan, slate. The color drives the dot on each row, the bullet on the group header, and the swatch in the Fleet View **Tags** filter. |
|
||||
| **Scope** | Per-node. The same name can exist on two different nodes with two different colors; the fleet-stop card matches by name across nodes. |
|
||||
| **Limit** | 50 labels per node. The **New label** primary button switches to **Limit reached** when you hit the cap. |
|
||||
| **Limit** | 50 labels per node. The **+ New label** button switches to **Limit reached** when you hit the cap. |
|
||||
|
||||
## Where labels appear
|
||||
|
||||
### Sidebar grouping
|
||||
|
||||
The stack list is split into one collapsible section per label. Group headers render in uppercase mono with a count chip on the right (`MEDIA 6`). Order is fixed: a `★ PINNED` group first if any stacks are pinned, then label groups sorted by stack count descending and by label name ascending, then `UNLABELED` last for stacks that carry no label. A stack tagged with two labels appears in both groups, the same row twice. Search and the **All / Up / Down / Updates** filter chips above the list operate on rows inside whichever groups are expanded.
|
||||
The stack list is split into one collapsible section per label. Group headers render in uppercase mono with a count chip on the right (`MEDIA 7`). Order is fixed: a `★ PINNED` group first if any stacks are pinned, then label groups sorted by stack count descending and by label name ascending, then `UNLABELED` last for stacks that carry no label. A stack tagged with two labels appears in both groups. Search and the **All / Up / Down / Updates** filter chips above the list operate on rows inside whichever groups are expanded.
|
||||
|
||||
Every row also carries up to three colored trailing dots that mirror the assigned labels. Beyond three, an additional `+N` counter appears next to the dots so the row never grows unbounded.
|
||||
|
||||
@@ -38,11 +39,12 @@ Every row also carries up to three colored trailing dots that mirror the assigne
|
||||
|
||||
The [Fleet View](/features/fleet-view) overview toolbar carries a **Filters** popover with a **Tags** multi-select. The dropdown lists every label that exists on any node in the fleet, with each entry rendered as a colored dot plus the label name. Selecting one or more tags filters the node cards to nodes that contain at least one stack with that label.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/fleet-tags-filter.png" alt="Fleet Overview Filters popover open. The popover shows four sections (Status, Type, Severity, Tags). The Tags multi-select is expanded into a dropdown with five options: Media, Network, Prod, staging, Utilities, each prefixed by a small colored dot." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/fleet-tags-filter.png"
|
||||
alt="Fleet Overview Filters popover open. The popover shows four sections: Status, Type, Severity, and Tags. The Tags multi-select is expanded into a dropdown listing five labels (Database, Media, Network, Prod, Utilities), each prefixed by a small colored dot."
|
||||
/>
|
||||
|
||||
The Tags filter aggregates label rows across nodes by name, so a label called `prod` that only exists on one of four nodes still shows up in the dropdown but the filter resolves to that single node.
|
||||
A label that exists on only one of four nodes still appears in the dropdown; selecting it resolves to that single node.
|
||||
|
||||
## Working with labels
|
||||
|
||||
@@ -50,15 +52,21 @@ The Tags filter aggregates label rows across nodes by name, so a label called `p
|
||||
|
||||
**Settings · Organization · Labels** is the canonical place to create, rename, recolor, and delete labels. The masthead shows a `LABELS N/50` counter so you can see how close the active node is to the cap, and a per-row stack count tells you how many stacks currently carry each label.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/settings-labels.png" alt="Settings page open on the Organization · Labels section. The right pane shows a 'Per-node labels for stacks and containers.' description, a 'New label' primary button, and three label rows: Media (purple dot, 6 stacks), Network (orange dot, 3 stacks), Utilities (slate dot, 5 stacks). The masthead shows the LABELS 3/50 stat." />
|
||||
</Frame>
|
||||
<Note>
|
||||
The Labels panel requires the `labels` capability on the active node. If the active node does not advertise this capability, a lock card appears instead of the label list. Switching to a node that supports labels, or updating the node, resolves this.
|
||||
</Note>
|
||||
|
||||
<img
|
||||
src="/images/stack-labels/settings-labels.png"
|
||||
alt="Settings page open on the Organization section with Labels selected in the sidebar. The main panel shows the heading Labels with a LABELS 5/50 stat in the top right. Five label rows are listed: Database (teal dot, 1 stack), Media (blue dot, 7 stacks), Network (amber dot, 3 stacks), Prod (amber dot, 0 stacks), Utilities (gray dot, 4 stacks). A cyan plus New label button sits in the top right."
|
||||
/>
|
||||
|
||||
Hover any row to reveal a **Pencil** edit icon and a destructive **Trash** icon on the trailing edge. The edit dialog shares its chrome with the create dialog: the kicker reads `LABELS · NEW` for a new label or `LABELS · EDIT` when you opened it from the pencil, the body has a single `Label name` input plus the ten color swatches, and the footer has **Cancel** and **Create** (or **Save**) buttons.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/create-label-dialog.png" alt="Create label modal. Kicker reads 'LABELS · NEW', title 'Create label', subtitle 'Manage label properties'. The body has a 'Label name' text input above a Color section with ten circular swatches arranged in a wrap (nine in the first row, slate alone in the second). Footer buttons are Cancel and a disabled Create." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/create-label-dialog.png"
|
||||
alt="Create label modal centered over the blurred Settings page. The kicker reads 'LABELS · NEW' and the title reads 'Create label'. The body has a focused Label name text input followed by a Color section showing ten circular swatches in two rows: teal, blue, purple, rose, amber, green, orange, pink, cyan in the first row, and slate alone in the second. The footer shows Cancel and Create buttons."
|
||||
/>
|
||||
|
||||
Deleting a label opens a destructive confirmation with the kicker `LABELS · DELETE · IRREVERSIBLE` and the body line `Removes the label from every stack across the fleet.` There is no undo: the label row is dropped, every assignment row pointing to it is dropped, and the affected stacks fall back to whatever other labels they still carry. Stacks left with no remaining labels move into the `UNLABELED` group on the next sidebar refresh.
|
||||
|
||||
@@ -69,47 +77,61 @@ Right-clicking a stack in the sidebar (or using the three-dot kebab menu on its
|
||||
- **New label** drops an inline form into the same submenu (text input with placeholder `Label name`, the ten color swatches, **Create** / **Cancel** buttons). Submitting creates the label on this node and assigns it to the stack in a single round trip. The entry hides itself once the node hits 50 labels.
|
||||
- **Manage labels...** sends you to **Settings · Organization · Labels** for bulk renames, recolors, and deletions.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/context-menu-labels.png" alt="Right-click context menu on a sidebar stack row. The Labels submenu is open and shows three label rows (Media with a checkmark on the right, Network, Utilities), a separator, a + New label entry, and a Manage labels... entry." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/context-menu-labels.png"
|
||||
alt="Stack context menu open on the Home page with the Labels submenu expanded. The submenu lists five labels: Database, Media (with a checkmark indicating it is assigned to this stack), Network, Prod, and Utilities, each with a colored dot. Below a separator are a plus New label entry and a Manage labels link."
|
||||
/>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/inline-create-form.png" alt="The Labels submenu after the user clicked New label. The submenu now shows a text input with the placeholder 'Label name', a row of ten colored circles (teal selected by default), and a row of two buttons labelled Create (disabled while the input is empty) and Cancel." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/inline-create-form.png"
|
||||
alt="The Labels submenu showing the inline create form after clicking New label. The submenu shows a focused Label name text input followed by a row of ten colored circles and a row of two buttons: Create (disabled while the name field is empty) and Cancel."
|
||||
/>
|
||||
|
||||
A stack can carry multiple labels and will then appear under each label's group in the sidebar. There is no per-stack label cap; the only cap is the per-node total of 50.
|
||||
|
||||
## Fleet · Fleet Actions
|
||||
|
||||
<Note>
|
||||
The Fleet Actions cards run admin-only on every tier. Operator and viewer roles see the cards but cannot apply them.
|
||||
The label-based Fleet Actions require the admin role. All other roles can view the cards but cannot execute them.
|
||||
</Note>
|
||||
|
||||
Two cards in the **Fleet · Actions** tab use labels to drive cross-node operations. See [Fleet Actions](/features/fleet-actions) for the full reference.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-labels/fleet-actions.png" alt="Fleet Actions tab with two cards side by side. Left card 'Stop fleet by label' (rose accent rail) has a Stack label combobox containing 'Media' and a Stop fleet button beneath. Right card 'Bulk label assign' (purple accent rail) has a label source row with a highlighted Media pill, a target-stacks list grouped by node with several stacks ticked, a per-node create-or-reuse preview, and an Apply button." />
|
||||
</Frame>
|
||||
<img
|
||||
src="/images/stack-labels/fleet-actions.png"
|
||||
alt="Fleet Actions tab. The left column shows the bottom portion of the Prune fleet-wide card and below it the Stop by label card (kicker FLEET · ACTIONS · STOP BY LABEL) with a DESTRUCTIVE badge, DRY RUN and STOP FLEET buttons, and a label name input field. The right column shows the Bulk label assign card with five label pills (Database, Media, Network, Prod, Utilities) and a scrollable stack checklist grouped by node."
|
||||
/>
|
||||
|
||||
### Stop fleet by label
|
||||
|
||||
Type a stack label name; Sencho fans the request out to every node and stops every stack on that node assigned a stack label with the same name. This action targets stack labels only, never node labels. The picker queries each reachable node for its own stack labels, so a label that exists only on a remote node still appears, listed once with its combined stack and node counts (and the carrying node names) so the scope is unmistakable. When a node cannot be reached, the picker notes that the suggestions may be incomplete. The result list shows a per-node breakdown with success and failure counts, and the helper line under the input states the scope: `Stops every stack assigned this stack label on every reachable node across the fleet. Node labels are not used by this action.` A confirmation modal titled `Stop all stacks with the stack label "<name>"?` with the **Stop fleet** primary action runs the action.
|
||||
Type a stack label name into the input field. Sencho fans the request out to every node and stops every stack on that node assigned a stack label with the same name.
|
||||
|
||||
A node that carries no matching stack label is shown as such, and a node Sencho could not reach is reported as unreachable, so a partial-fleet stop is observable rather than silent. Unreachable nodes never block the stop on the reachable ones.
|
||||
**Live preview.** As you type (debounced 500 ms), a preview lists which stacks on which nodes would be stopped. The **Stop fleet** button stays disabled until the preview resolves to at least one matching stack.
|
||||
|
||||
<img
|
||||
src="/images/stack-labels/fleet-stop-preview.png"
|
||||
alt="Stop by label card with 'Media' typed in the label input. The badge reads 'DESTRUCTIVE · 7 STACKS · 1 NODES'. Below the input a preview panel shows the label Media with the entry 'Local · 7 stacks · 1 node', and beneath that a list of six stacks: bazarr, plex, radarr, seerr, sonarr, tautulli, each tagged UP and LOCAL."
|
||||
/>
|
||||
|
||||
**Dry run.** Click **Dry run** to simulate the operation without stopping any containers. The card shows the per-node breakdown labelled as a dry run and records the resolved target list. A completed dry run also unblocks the **Stop fleet** button when the live preview endpoint is unavailable.
|
||||
|
||||
This action targets stack labels only, never node labels. A confirmation modal shows the concrete list of nodes and stacks before the stop runs. A node with no matching label is noted as such; an unreachable node is reported but never blocks the stop on reachable nodes.
|
||||
|
||||
### Bulk label assign
|
||||
|
||||
Pick a label that exists anywhere in the fleet, then tick the stacks you want across one or more nodes (grouped by node, with a filter and per-node select-all). The preview shows, per node, whether the label will be created or reused; **Apply** adds the label to each chosen stack on its node, creating it there first with the same name and color if the node does not have it yet. Existing labels on the selected stacks are preserved. The confirmation modal summarizes the blast radius before you commit, and the result list breaks down per node, noting whether the label was created or reused.
|
||||
Pick a label that exists anywhere in the fleet, then tick the stacks you want across one or more nodes (grouped by node, with a filter and per-node select-all).
|
||||
|
||||
Because labels are node-local, each target node uses its own copy of the label rather than the control node's, so propagating a label keeps every node's label table self-consistent.
|
||||
When the chosen label name exists with different colors on different nodes, the card shows a notice: "This label uses different colors on different nodes. The shown color is applied where it is created." Color resolution order is: the local node's color first, then the most common color across nodes, then the first seen.
|
||||
|
||||
**Apply** adds the label to each chosen stack on its node, creating it there first with the chosen name and color if the node does not have it yet. Existing labels on the selected stacks are preserved. The confirmation modal summarizes the blast radius before you commit, and the result list breaks down per node, noting whether the label was created or reused.
|
||||
|
||||
## Limits and rules
|
||||
|
||||
- **50 labels per node.** Settings hides the **New label** button at the cap; the inline `+ New label` entry in the stack menu hides itself too.
|
||||
- **50 labels per node.** Settings hides the **+ New label** button at the cap; the inline `New label` entry in the stack menu hides itself too.
|
||||
- **Names are unique per node**, case-sensitive. The same name on two nodes is two separate label rows. Cross-node fleet stop and bulk assign both match by name across nodes; each node resolves the name to its own label (and bulk assign creates it there if missing).
|
||||
- **Allowed name characters**: letters, digits, spaces, and hyphens. Empty names and names beyond 30 characters are rejected at the API.
|
||||
- **Bulk-action concurrency**: only one label-driven bulk action can run on a single node at a time. A second concurrent attempt against the same node returns HTTP 429 and the operator sees an error toast; the in-flight action keeps running.
|
||||
- **Role visibility**: label authoring is open to every signed-in role. Sidebar grouping, trailing dots on stack rows, the **Settings · Organization · Labels** panel, the inline create form in the stack menu, and the Fleet View **Tags** filter all work for every user.
|
||||
- **Permissions**: Every signed-in user can view labels: sidebar grouping, trailing dots on stack rows, the Settings panel (read-only), and the Fleet View Tags filter all work for every user. Creating, renaming, recoloring, and deleting labels requires the **admin** or **node-admin** role. The label-based Fleet Actions (Stop fleet by label, Bulk label assign) require the **admin** role.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -120,23 +142,26 @@ Because labels are node-local, each target node uses its own copy of the label r
|
||||
<Accordion title="The trailing colored dots are missing on stack rows">
|
||||
A stack row only renders trailing dots when at least one label is assigned to that stack. Right-click the row, open the **Labels** submenu, and tick at least one label; the dots appear on the next sidebar refresh. If a stack already has labels assigned but the dots still do not appear, check that the active node is the one that owns the assignments. Labels are per-node, so switching the node switcher to a different instance shows that instance's assignments only.
|
||||
</Accordion>
|
||||
<Accordion title="`+ New label` is missing from the stack context menu">
|
||||
The active node already has 50 labels (the per-node cap). Both the inline `New label` entry in the stack submenu and the **New label** button in **Settings · Organization · Labels** hide themselves at the cap. Delete an unused label or rename an existing one to free a slot.
|
||||
<Accordion title="New label is missing from the stack context menu">
|
||||
The active node already has 50 labels (the per-node cap). Both the inline New label entry in the stack submenu and the + New label button in **Settings · Organization · Labels** hide themselves at the cap. Delete an unused label or rename an existing one to free a slot.
|
||||
</Accordion>
|
||||
<Accordion title="The Tags filter does not list a label I just created">
|
||||
The Tags filter aggregates labels across every node in the fleet by name. If the new label only exists on one node and that node was offline at the moment the page loaded, the dropdown may not include it. Refresh **Fleet · Overview** with the **Refresh** button in the toolbar to repull node state.
|
||||
The Tags filter aggregates labels across every node in the fleet by name. If the new label only exists on one node and that node was offline at the moment the page loaded, the dropdown may not include it. Refresh **Fleet · Overview** with the toolbar refresh button to repull node state.
|
||||
</Accordion>
|
||||
<Accordion title="`Stop fleet by label` reports `No node carries a stack label by that name`">
|
||||
<Accordion title="The Settings Labels panel shows a lock card instead of the label list">
|
||||
The active node does not advertise the `labels` capability. Switch to a node that supports labels, or update the node to a version that includes this capability.
|
||||
</Accordion>
|
||||
<Accordion title="Stop fleet by label reports no matching stacks">
|
||||
Stack labels are per-node, so the fleet-stop matches by name across nodes. If the stack label you typed only exists on one node and you typed the wrong case (`prod` versus `Prod`), no node will match. The picker queries each reachable node for its own stack labels; node labels never appear there. A label on a node Sencho cannot currently reach will not be suggested, and the picker flags that the list may be incomplete. Pick from the suggestion list rather than typing freehand to avoid case mistakes.
|
||||
</Accordion>
|
||||
<Accordion title="`Bulk label assign` did not remove the old labels on my stacks">
|
||||
<Accordion title="Bulk label assign did not remove the old labels on my stacks">
|
||||
By design it never does: the card only adds the label you picked, leaving each stack's other labels intact. There is no clear or replace mode. To remove or swap a stack's labels, edit them from that stack's own **Labels** menu.
|
||||
</Accordion>
|
||||
<Accordion title="A label did not appear on a remote node after bulk assign">
|
||||
Confirm the node was reachable when you applied: an unreachable node is shown in the target list and reported in the per-node results rather than silently skipped. If the node was reachable, the label is created there by name with the chosen color and assigned; re-run to retry any node that failed.
|
||||
</Accordion>
|
||||
<Accordion title="The Fleet Actions cards return an error when I click Apply">
|
||||
The cards run admin-only. Confirm the active user has the admin role under **Settings · Users**; operator and viewer roles see the cards rendered but cannot apply them. All other Stack Labels surfaces (sidebar grouping, trailing dots, the Settings panel, the inline create form, the Fleet View Tags filter) work for every role.
|
||||
<Accordion title="The Fleet Actions cards return an error when I click Apply or Stop fleet">
|
||||
The label-based Fleet Actions require the admin role. Confirm the active user has the admin role under **Settings · Users**; all other roles see the cards but cannot execute them.
|
||||
</Accordion>
|
||||
<Accordion title="Deleting a label removed it from every stack">
|
||||
Working as designed. The destructive confirmation reads `Removes the label from every stack across the fleet.` The label row and every assignment row that pointed to it are dropped in a single transaction. There is no undo; recreate the label by name and color and reassign the affected stacks if you need to recover.
|
||||
|
||||
@@ -135,10 +135,15 @@ Each row leads with a two-character status indicator that summarizes the stack's
|
||||
| Indicator | Meaning |
|
||||
|-----------|---------|
|
||||
| `UP` (green) | All containers running |
|
||||
| `DN` (red) | One or more containers exited |
|
||||
| (blank) | No containers running, status unknown, or the stack has never been deployed |
|
||||
| `PARTIAL` (orange) | Some containers running, at least one exited |
|
||||
| `DN` (red) | All containers stopped or exited |
|
||||
| (blank) | No containers running, never deployed, or status unknown |
|
||||
|
||||
A small fuchsia dot to the right of the stack name flags that an image update is available. A pulsing brand-color dot on the **Git Source** affordance signals the upstream branch has moved ahead of the working copy.
|
||||
Additional indicators appear to the right of the stack name:
|
||||
|
||||
- A pulsing fuchsia dot flags that an image update is available.
|
||||
- A muted alert icon replaces the dot when the update check ran but failed to reach the registry or errored.
|
||||
- A branch icon signals that the Git source's upstream branch has moved ahead of the working copy.
|
||||
|
||||
### Search and filter chips
|
||||
|
||||
@@ -239,19 +244,20 @@ The structured viewer holds up to 10,000 lines; older entries are dropped from t
|
||||
|
||||
## Anatomy panel
|
||||
|
||||
The right column of the stack view is a two-tab panel: **Anatomy** is the read-only summary of what the compose file declares, and **Activity** is the audit log for that stack.
|
||||
The right column of the stack view is a tabbed panel. Tabs appear only when the data they need is available.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-view/anatomy-panel.png" alt="Anatomy panel with services, ports, volumes, restart, env_file, network, and source rows, plus a footer link for the exposed port" />
|
||||
<img src="/images/stack-view/anatomy-panel.png" alt="Anatomy panel showing all eight tabs: Anatomy, Activity, Dossier, Drift, Environment, Networking, Doctor, and Storage, with the Networking tab active" />
|
||||
</Frame>
|
||||
|
||||
The header carries:
|
||||
The **Files** and **Edit** buttons sit to the right of the tab row.
|
||||
|
||||
- **Anatomy / Activity tabs.** Activity is documented on its own page; see [Stack Activity](/features/stack-activity).
|
||||
- **Files** button. Opens the in-stack file explorer; see [Stack File Explorer](/features/stack-file-explorer).
|
||||
- **Edit** button. Slides the Monaco editor over the panel for inline compose and env edits.
|
||||
- **Files**: opens the in-stack file explorer; see [Stack File Explorer](/features/stack-file-explorer).
|
||||
- **Edit**: slides the Monaco editor over the panel for inline compose and env edits.
|
||||
|
||||
Each row inside the **Anatomy** tab maps one compose concept to the value it resolves to right now:
|
||||
### Anatomy
|
||||
|
||||
Each row maps one compose concept to the value it resolves to right now:
|
||||
|
||||
- **Services**: Cyan pills, one per key under `services:`.
|
||||
- **Ports**: `{host} → {container}/{proto}` per service, host port highlighted.
|
||||
@@ -263,7 +269,58 @@ Each row inside the **Anatomy** tab maps one compose concept to the value it res
|
||||
|
||||
A footer card under the rows surfaces the first published port as a clickable **EXPOSED** link, so you can jump straight to the running app.
|
||||
|
||||
If an image update is available for the primary service, an inline banner appears below the rows with the version bump (`27.1.4 → 27.1.5`), risk classification (`safe · patch`, `minor`, or `major · review required`), and an **apply** button that runs the update on this stack. Major bumps show a rose banner and require explicit review before applying.
|
||||
If an image update is available for the primary service, an inline banner appears below the rows with the version bump (`27.1.4 → 27.1.5`), risk classification (`safe · patch`, `minor`, or `major · review required`), and an **apply** button. Major bumps show a rose banner and require explicit review before applying.
|
||||
|
||||
### Activity
|
||||
|
||||
A timestamped audit trail of every deploy, restart, stop, update, and rollback on this stack, attributed to the user or subsystem that triggered it. See [Stack Activity](/features/stack-activity).
|
||||
|
||||
### Dossier
|
||||
|
||||
A generated summary of what the stack declares (services, ports, volumes, network, source) combined with a free-text notes section where you can record purpose, owner, access URLs, backup procedure, and recovery steps. Exports to Markdown. See [Stack Dossier](/features/stack-dossier).
|
||||
|
||||
### Drift
|
||||
|
||||
Compares the running containers against what the compose file declares. Reports services, images, and ports that have diverged from the compose definition since the last deploy. See [Stack Drift](/features/stack-drift).
|
||||
|
||||
### Environment
|
||||
|
||||
An inventory of every environment variable the stack uses, organized by status:
|
||||
|
||||
- **Present**: defined in the env file and referenced by the compose file.
|
||||
- **Missing**: referenced in the compose file but absent from the env file.
|
||||
- **Duplicate**: defined more than once across env files or sources.
|
||||
- **Unpersisted**: injected at runtime (shell export) but not saved to any file.
|
||||
- **Unused**: defined in the env file but never referenced by any service.
|
||||
|
||||
Variables that look like secrets (tokens, passwords, keys) are marked with a lock icon. When the stack references multiple env files, use the file picker in the tab header to switch between them.
|
||||
|
||||
### Networking
|
||||
|
||||
Shows the stack's defined networks, external network attachments, and the driver for each, along with an exposure-intent classification per service. A Runtime Drift row confirms whether the running network topology matches what the compose file declares. See [Compose Networking](/features/compose-networking).
|
||||
|
||||
### Doctor
|
||||
|
||||
Runs preflight checks against the compose file and reports findings grouped by severity:
|
||||
|
||||
| Severity | Meaning |
|
||||
|----------|---------|
|
||||
| **Blocker** | Deploy will fail or behave incorrectly as configured |
|
||||
| **High risk** | Significant misconfiguration or security concern |
|
||||
| **Warning** | Best-practice deviation worth reviewing |
|
||||
| **Info** | Informational observation, no action required |
|
||||
|
||||
Each finding includes the affected service, a description, and a remediation suggestion. Click the refresh button to rerun the checks. See [Compose Doctor](/features/compose-doctor).
|
||||
|
||||
### Storage
|
||||
|
||||
Analyzes the stack's volume and bind-mount configuration and reports a portability status:
|
||||
|
||||
- **Portable**: all mounts use named volumes or relative bind paths. The stack can be moved to another host without path changes.
|
||||
- **Partially portable**: some mounts use absolute host paths; others are portable.
|
||||
- **Node-bound**: one or more mounts pin the stack to a specific host path, socket, or symlink that may not exist on another node.
|
||||
|
||||
Each mount row shows its type (bind, named, anonymous, tmpfs, socket), permissions (rw or ro), the host path, the container path, and whether the source path exists on disk. See [Compose Storage](/features/compose-storage).
|
||||
|
||||
### Editing compose.yaml
|
||||
|
||||
@@ -275,6 +332,8 @@ Select a stack and click **Start** (or **Restart** when it's already running) in
|
||||
|
||||
For deploy progress, error surfacing, atomic backups, and rollback, see [Atomic Deployments](/features/atomic-deployments).
|
||||
|
||||
If a deploy policy is configured, Sencho checks it before the deploy runs. When a policy is violated, a dialog lists the triggered rules and blocks the deploy. Admin users see a **Deploy anyway** button to override the block. See [Deploy Enforcement](/features/deploy-enforcement) for configuring policies.
|
||||
|
||||
## Controlling a running stack
|
||||
|
||||
The stack header groups actions by frequency of use. The most common action is the filled cyan button on the left, everyday secondaries sit next to it, and destructive or occasional actions are tucked behind the **More actions** overflow.
|
||||
@@ -395,10 +454,11 @@ Click the **Scan stacks folder** icon button to the right of **Create Stack**. S
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service action returns a "service not found" error
|
||||
|
||||
The service name used in the action must match the `services:` key in the stack's `compose.yaml`. This error occurs when no running containers match that service name, either because the service was never deployed or because the compose file defines a different name. Verify the service key in your compose file and ensure the stack has been deployed at least once so containers exist for that service.
|
||||
|
||||
### A published port link opens but the service does not load
|
||||
|
||||
The link targets the active node's host and the published host port. It cannot reach a service bound only to `127.0.0.1` on the node, or one published on a port your browser cannot route to. For a remote node, the link uses that node's host; if the service lives at a different address, open it there directly. A port that is not published to a host port has no link.
|
||||
<AccordionGroup>
|
||||
<Accordion title="Service action returns a 'service not found' error">
|
||||
The service name used in the action must match the `services:` key in the stack's `compose.yaml`. This error occurs when no running containers match that service name, either because the service was never deployed or because the compose file defines a different name. Verify the service key in your compose file and ensure the stack has been deployed at least once so containers exist for that service.
|
||||
</Accordion>
|
||||
<Accordion title="A published port link opens but the service does not load">
|
||||
The link targets the active node's host and the published host port. It cannot reach a service bound only to `127.0.0.1` on the node, or one published on a port your browser cannot route to. For a remote node, the link uses that node's host; if the service lives at a different address, open it there directly. A port that is not published to a host port has no link.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -3,7 +3,9 @@ title: Configuration
|
||||
description: Environment variables, volume mounts, and the 1:1 path rule.
|
||||
---
|
||||
|
||||
Sencho is configured entirely through environment variables and Docker volume mounts. There is no config file to edit inside the container.
|
||||
Sencho's deployment is configured through environment variables and Docker volume mounts set on the Sencho container itself: there is no config file to edit inside the container. This page covers that deployment layer.
|
||||
|
||||
Operational settings (host alerts, data retention, image-update automation, mesh networking, registries, notifications, and more) are configured in the app after first boot, in the Settings Hub. See the [Settings Reference](/reference/settings) for that runtime layer.
|
||||
|
||||
## Required environment variables
|
||||
|
||||
@@ -29,7 +31,20 @@ When you point `COMPOSE_DIR` at a directory, Sencho expects each stack to live i
|
||||
| `SENCHO_USER` | *(unset)* | When set to a username present inside the container (`sencho` is pre-created for this purpose), the entrypoint drops privileges to that user at startup instead of running as `root`. See [Running as a non-root user](#running-as-a-non-root-user) below. |
|
||||
| `API_RATE_LIMIT` | `200` | Global API requests per minute per user session. Applies in production only; development uses a fixed higher cap. Authenticated requests are keyed by user ID, unauthenticated by IP. Internal node-to-node traffic bypasses this limit. |
|
||||
| `API_POLLING_RATE_LIMIT` | `300` | Rate limit for dashboard polling endpoints, in requests per minute. Applies in production only; development uses a fixed higher cap. Raise it for environments with many concurrent browser sessions behind shared NAT. |
|
||||
| `SENCHO_COMPOSE_STALL_TIMEOUT_MS` | `600000` | Idle-output backstop for deploy and update compose steps (pull and recreate). If a step produces no output for this long while still running, Sencho treats it as stalled and stops it, so a hung image pull surfaces a clear failure and the in-app recovery actions instead of spinning. Raise it on slow links or for heavy local image builds. |
|
||||
|
||||
## Advanced environment variables
|
||||
|
||||
These tune optional subsystems. Most deployments never set them; the defaults are sensible.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `TRIVY_BIN` | *(unset)* | Path to a host-installed [Trivy](/operations/trivy-setup) binary for image vulnerability scanning. Sencho prefers a managed install under `DATA_DIR/bin/trivy`, then this path, then `trivy` on `PATH`. |
|
||||
| `SENCHO_MESH_SUBNET` | *(auto)* | CIDR for this node's `sencho_mesh` network. When unset, Sencho picks the first free `/24` from its candidate list or adopts an existing mesh subnet. Set one only to avoid an overlap with another network on the host. See [Sencho Mesh](/features/sencho-mesh). |
|
||||
| `GITSOURCE_MAX_CLONE_BYTES` | `104857600` | Maximum bytes a single [Git Source](/features/git-sources) clone may download before it is aborted (100 MB). A shallow Compose clone is tiny; raise it only if you track Compose files in a legitimately large repository. |
|
||||
| `SENCHO_PUBLIC_URL` | *(request host)* | Set on the primary instance. Its externally reachable `http(s)://` URL, no trailing slash, baked into pilot enrollment so remote agents dial the public hostname rather than the address the admin used at setup. |
|
||||
| `SENCHO_COMPOSE_STALL_TIMEOUT_MS` | `600000` | Idle-output backstop for deploy and update Compose steps (pull and recreate). If a step produces no output for this long while still running, Sencho stops it so a hung image pull surfaces a clear failure and the in-app recovery actions instead of spinning. Raise it on slow links or for heavy local image builds. |
|
||||
|
||||
Running a remote host as a pilot agent uses four more variables (`SENCHO_MODE`, `SENCHO_PRIMARY_URL`, `SENCHO_ENROLL_TOKEN`, and `SENCHO_PILOT_CA_FILE`), set only on the remote agent container. Sencho bakes them into the enrollment Compose file it generates, so you rarely write them by hand. See [Pilot Agent](/features/pilot-agent) for the full enrollment walkthrough.
|
||||
|
||||
## Listen port
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ The **Home** view is the default landing page. It is designed for a fast operati
|
||||
- The activity panel shows **Fleet Heartbeat** when remote nodes exist, or **Stack Restarts (7d)** on a local-only install.
|
||||
- **Recent Alerts** shows the latest notification feed and includes **Clear All Notifications** when there is anything to clear.
|
||||
|
||||
The top navigation includes **Home**, **Fleet**, **Resources**, **App Store**, and **Logs** on the standard dashboard surface. Depending on license, role, and node context, it can also include **Update**, **Schedules**, **Console**, and **Audit**.
|
||||
The top navigation strip starts with **Home**, **Fleet**, **Resources**, **Security**, and **App Store**. Additional operator views (**Logs**, **Update**, **Schedules**, **Console**, and **Audit**) appear based on your role and license tier. Fleet-wide views describe the control instance, so they are hidden while a remote node is active.
|
||||
|
||||
## Stack workspace
|
||||
|
||||
@@ -52,7 +52,7 @@ Opening a stack gives you the day-to-day workspace:
|
||||
- Running stacks expose **Restart**, **Stop**, and **Update**; stopped stacks expose **Start** and **Update**. The overflow menu holds less frequent actions such as rollback, config scan, and delete.
|
||||
- Container rows show health, uptime, published ports, live CPU and memory, network activity, logs, and service actions.
|
||||
- The logs panel can run in **Structured** mode or **Raw terminal** mode.
|
||||
- The right panel switches between **Anatomy** and **Activity**, with **Files** and **Edit** controls for browsing stack files and editing compose or env content.
|
||||
- The right panel provides tabs for **Anatomy**, **Activity**, **Dossier**, **Drift**, **Environment**, **Networking**, **Doctor**, and **Storage**, with **Files** and **Edit** controls for browsing stack files and editing compose or env content.
|
||||
|
||||
## Fleet operations
|
||||
|
||||
@@ -64,7 +64,7 @@ The **Fleet** view is the multi-node command center. The masthead summarizes onl
|
||||
|
||||
The Fleet toolbar includes **Check Updates**, **Refresh**, and **Add node** for admins. The **Overview** tab supports search, sort, status filters, label filters, and a Grid or Topology view. Node cards show online state, resource use, container counts, version state, update actions, and direct drill-down into stacks on that node.
|
||||
|
||||
Fleet also provides dedicated tabs for snapshots, node status, blueprint deployments, traffic management, federation, fleet actions, and secrets. Some fleet features require Admiral. See [Licensing](/features/licensing) for the full tier breakdown.
|
||||
Beyond **Overview**, Fleet provides tabs for **Snapshots**, node **Status**, a dependency **Map**, blueprint **Deployments**, mesh **Routing**, **Federation**, fleet **Actions**, and **Secrets**. Some fleet tabs require Admiral. See [Licensing](/features/licensing) for the full tier breakdown.
|
||||
|
||||
## Resources, templates, and logs
|
||||
|
||||
@@ -78,11 +78,21 @@ The **App Store** lets you search templates, filter by category, open a deploy s
|
||||
|
||||
The **Logs** view aggregates logs across stacks on the active node. It includes a live masthead, event counters, search, stack filters, stream filters, level filters, pause and resume controls, and download support for the filtered buffer.
|
||||
|
||||
## Settings and security
|
||||
## Security
|
||||
|
||||
Settings are grouped by **Personal**, **Access**, **Infrastructure**, **Monitoring**, **Notifications**, **Automation**, **Organization**, **Security**, **Operations**, and **Help**. Some sections are global to the control instance, while others are scoped to the active node.
|
||||
The **Security** view is a node-scoped review surface for the container images on the active node. The masthead reports the overall posture, how many images have been scanned, and whether the scanner is installed.
|
||||
|
||||
Use Settings to manage account security, licensing, users, SSO, API tokens, host thresholds, node registration, alert delivery, security scanning, template registry settings, diagnostics, and build metadata. The [Configuration](/getting-started/configuration) guide covers the host and environment settings that matter before first deploy.
|
||||
<Frame>
|
||||
<img src="/images/introduction/security-overview.png" alt="Sencho Security view with the posture masthead, action summary, review queue, risk trend chart, and the tab strip" />
|
||||
</Frame>
|
||||
|
||||
Tabs cover the **Overview** charts, per-image findings under **Images**, **Compose risks** read from your Compose files, embedded **Secrets** detection, deploy-blocking **Policies**, **Suppressions** for findings you have accepted, scan **History**, and **Scanner setup**. Scanning is powered by Trivy and installs from **Scanner setup** in one step. See [Vulnerability Scanning](/features/vulnerability-scanning) for the full workflow.
|
||||
|
||||
## Settings
|
||||
|
||||
Settings are grouped by **Personal**, **Access**, **Infrastructure**, **Monitoring**, **Notifications**, **Automation**, **Organization**, **Operations**, and **Help**. Some groups are global to the control instance, while others, such as **Monitoring**, are scoped to the active node.
|
||||
|
||||
Use Settings to manage account security, licensing, users, SSO, API tokens, host thresholds, node registration, alert delivery, template registry settings, diagnostics, and build metadata. The [Configuration](/getting-started/configuration) guide covers the host and environment settings that matter before first deploy.
|
||||
|
||||
## Typical workflow
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ The Compose directory must be mounted at the **same path** inside and outside th
|
||||
Open `http://localhost:1852` in a browser. On a fresh install you land on the **Cold start** card, where Sencho asks you to create the first admin account.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/quickstart/setup-cold-start.png" alt="Sencho first-boot Cold start card with Username, Password, and Confirm password fields, and an Initialize console button" />
|
||||
<img src="/images/quickstart/setup-cold-start.png" alt="Sencho first-boot Cold start card with Username, Password, and Confirm password fields, a password strength indicator, and an Initialize console button" />
|
||||
</Frame>
|
||||
|
||||
Pick a username, choose a password, confirm it, and click **Initialize console**. The username placeholder shows `admin`. The password must be at least eight characters, and the strength indicator labels the password **Weak**, **Fair**, or **Strong** as you type.
|
||||
@@ -90,7 +90,7 @@ Pick a username, choose a password, confirm it, and click **Initialize console**
|
||||
Sencho then runs a short **environment preflight**: it confirms the Docker engine and Compose plugin are reachable, the compose directory is writable and mounted at a matching host path, the dashboard is behind TLS, and there is disk headroom. Anything that needs attention shows an inline fix. The checks never block you, so click **Enter Sencho** to continue; you can re-run them anytime from **Settings · Recovery**.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/quickstart/setup-environment.png" alt="Sencho first-boot environment preflight with check rows for Docker engine, Docker Compose, Compose directory, path mapping, TLS, and disk space, each showing an OK or warning result, a Re-run button, and an Enter Sencho button" />
|
||||
<img src="/images/quickstart/setup-environment.png" alt="Sencho first-boot environment preflight with check rows for Docker engine, Docker Compose, Compose directory, path mapping, TLS, and disk space, each showing a pass or warning status with inline remediation text, a Re-run button, and an Enter Sencho button" />
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
@@ -99,15 +99,15 @@ Sencho then runs a short **environment preflight**: it confirms the Docker engin
|
||||
|
||||
## After signing in
|
||||
|
||||
You land on **Home**, the default operational view. The health masthead reports **Healthy**, **Degraded**, or **Critical**, names the active node, shows how many nodes are registered, and lists any signals that need attention. The resource gauge strip tracks **CPU**, **Memory**, **Disk**, and **Network**. **Stack health** lists discovered stacks on the active node, sorted so exited or high-load stacks rise to the top.
|
||||
You land on **Home**, the default operational view. The health masthead reports **Healthy**, **Degraded**, or **Critical**, names the active node, shows how many nodes are registered, and lists any signals that need attention. The resource gauge strip tracks **CPU**, **Memory**, **Disk**, and **Network** with sparklines and threshold coloring. **Stack health** lists the active node's stacks, sorted by state then load, with uptime, CPU, memory, and a 10-minute CPU sparkline per row.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/quickstart/dashboard.png" alt="Sencho Home view with the health masthead, CPU, Memory, Disk, and Network gauges, Stack health, Configuration Status, an activity panel, and Recent Alerts" />
|
||||
<img src="/images/quickstart/dashboard.png" alt="Sencho Home view with the health masthead, resource gauges with sparklines, Stack health table, Configuration Status, Fleet Heartbeat panel, and Recent Alerts" />
|
||||
</Frame>
|
||||
|
||||
Below the stack table, **Configuration Status** summarizes notification delivery, alert rules, automation, account security, backups, host thresholds, and crash detection. The neighboring activity card shows **Fleet Heartbeat** when remote nodes exist, or **Stack Restarts (7d)** on a local-only install. **Recent Alerts** shows the latest notification feed and includes **Clear All Notifications** when there is anything to clear.
|
||||
Below the stack table, **Configuration Status** summarizes notifications, alerts, automation, security, backups, thresholds, and crash detection. The neighboring activity card shows **Fleet Heartbeat** when remote nodes exist, or **Stack Restarts (7d)** on a local-only install. **Recent Alerts** shows the latest notification feed and includes **Clear All Notifications** when there is anything to clear.
|
||||
|
||||
On the local node, the top navigation includes **Home**, **Fleet**, **Resources**, **App Store**, and **Logs**. Depending on license, role, and node context, it can also include **Update**, **Schedules**, **Console**, and **Audit**; hub-only views are hidden when a remote node is active. The right side of the top bar holds global search, notifications, and the profile menu entries **Settings**, **Documentation**, **Feedback**, **Appearance**, and **Log Out**.
|
||||
On the local node, the top navigation includes **Home**, **Fleet**, **Resources**, **Security**, **App Store**, and **Logs**. Depending on license, role, and node context, it can also include **Update**, **Schedules**, **Console**, and **Audit**; hub-only views are hidden when a remote node is active. The right side of the top bar holds global search, notifications, and the profile menu entries **Settings**, **Documentation**, **Feedback**, **Appearance**, and **Log Out**.
|
||||
|
||||
The left sidebar is the stack workspace. Below the Sencho brand, it starts with the node switcher, then **Create Stack**, a bulk-mode toggle, and **Scan stacks folder** for importing Compose projects added outside Sencho. Use **Search stacks...** with the **All**, **Up**, **Down**, and **Updates** chips to narrow the list. On a fresh install the stack list is empty until you create a stack or scan a populated `COMPOSE_DIR`.
|
||||
|
||||
|
||||
@@ -3,22 +3,28 @@ title: SSO Setup Guide
|
||||
description: Step-by-step instructions for connecting Sencho to your identity provider.
|
||||
---
|
||||
|
||||
SSO can be configured from the Settings UI or seeded via environment variables (shown below).
|
||||
SSO is configured from the dashboard at **Settings → Access → SSO** (admin only) or seeded via environment variables (shown below).
|
||||
|
||||
<Note>
|
||||
**Tier availability.** Custom OIDC and the Google, GitHub, and Okta preset providers are available on every tier, including Community. LDAP / Active Directory requires Admiral. See [Licensing & Billing](/features/licensing#feature-breakdown) for the full breakdown.
|
||||
</Note>
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sso/sso-quickstart-overview.png" alt="Settings > SSO panel showing all five provider cards with PROVIDERS and ENABLED stats in the masthead" />
|
||||
<img src="/images/sso/sso-quickstart-overview.png" alt="Settings → Access → SSO panel: five provider cards (Custom OIDC, Google, GitHub, Okta, LDAP / Active Directory) with PROVIDERS and ENABLED counts in the masthead" />
|
||||
</Frame>
|
||||
|
||||
Each provider is a collapsible card you switch on, fill in, and test in place. The masthead tracks how many providers are configured (**PROVIDERS**) and how many are switched on (**ENABLED**).
|
||||
|
||||
<Note>
|
||||
**What your users see.** Once a provider is enabled, it appears on the login page. The OIDC providers (Custom OIDC, Google, GitHub, Okta) show up as buttons under an **Or continue with** divider; LDAP adds a **Local / LDAP** toggle beside the **Sign in** heading. Password sign-in stays available alongside the SSO options.
|
||||
</Note>
|
||||
|
||||
## How to configure
|
||||
|
||||
You can wire SSO two ways. Both reach the same database row, and you can mix and match.
|
||||
|
||||
- **Environment variables** seed the SSO configuration the first time Sencho boots with that variable set. They are useful for infrastructure-as-code, fresh deployments, and disaster recovery. After a configuration row exists in the database, the database is authoritative; subsequent restarts do not re-read the env vars or overwrite changes you made in the UI.
|
||||
- **Settings > SSO** in the dashboard lets admins enable, edit, save, and remove providers without restarting. Changes apply immediately. Each provider card has a **Test Connection** button that validates connectivity before you commit (LDAP bind plus search for LDAP, OIDC discovery plus token endpoint reachability for OIDC).
|
||||
- **Settings → Access → SSO** in the dashboard lets admins enable, edit, save, and remove providers without restarting. Changes apply immediately. Each provider card has a **Test Connection** button that validates connectivity before you commit (LDAP bind plus search for LDAP, OIDC discovery plus token endpoint reachability for OIDC).
|
||||
|
||||
If you want a guided UI walkthrough rather than the env-var path below, jump to the [SSO feature page](/features/sso#configuration).
|
||||
|
||||
@@ -141,10 +147,10 @@ If your provider uses non-standard claim names, add claim mapping:
|
||||
- SSO_LDAP_DEFAULT_ROLE=viewer
|
||||
```
|
||||
|
||||
Once Sencho is running, open **Settings > SSO**, expand the LDAP card, and click **Test Connection**. Sencho binds with the service account, runs the search filter, and reports the result inline.
|
||||
Once Sencho is running, open **Settings → Access → SSO**, expand the LDAP card, and click **Test Connection**. Sencho binds with the service account, runs the search filter, and reports the result inline.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/sso/sso-quickstart-ldap-test.png" alt="LDAP provider card expanded showing the configuration form with example values, the Verify TLS certificate toggle, and Save and Test Connection buttons" />
|
||||
<img src="/images/sso/sso-quickstart-ldap-test.png" alt="LDAP / Active Directory card expanded in the SSO panel, showing the configuration form with example values, the Verify TLS certificate toggle, the Save and Test Connection buttons, and an inline error indicator after a failed test" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
@@ -172,7 +178,7 @@ By default, all SSO users are assigned the **Viewer** role. To grant Admin to sp
|
||||
This tells Sencho to check the `groups` claim in the OIDC ID token. If it contains `sencho-admins`, the user gets Admin. Roles are synced on every login, so removing a user from the admin group will demote them on their next sign-in.
|
||||
|
||||
<Note>
|
||||
Some providers (e.g., Okta, Zitadel) require custom scopes to include group claims in the ID token. You can configure additional scopes in the **Scopes** field in Settings > SSO, or via environment variable. The default is `openid email profile`.
|
||||
Some providers (e.g., Okta, Zitadel) require custom scopes to include group claims in the ID token. You can configure additional scopes in the **Scopes** field in Settings → Access → SSO, or via environment variable. The default is `openid email profile`.
|
||||
</Note>
|
||||
|
||||
## Full docker-compose.yml example with SSO
|
||||
|
||||
|
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 486 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 267 KiB |
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 288 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 317 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 334 KiB After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 194 KiB |