You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
S3-compatible backups (feat(backup): Add remote S3-compatible backup destinations #168, ui: Object stores, a base component library, and dark-mode fixes ui#79): backups now mirror to remote S3-compatible object storage (AWS, R2, B2, MinIO) on top of the always-local copy, best-effort so a remote outage never fails a backup. What/how/where is documented in docs/BACKUPS.md; object-storage secrets live in the credential manager, never the flat-file config. This also seeds the object store abstraction (external vs managed stores, a MinIO template so FlatRun can run its own S3 endpoint), specced in docs/OBJECT_STORES.md. Follow-up slices, still open: one-click managed deploy with auto-register, an object browser, deployment consumption, and store-to-store replication.
Dashboard & API performance (performance: Loading deployments and the dashboard is generally too slow #154): progressive / lazy loading for the deployments
list and dashboard instead of loading everything at once; profile and optimize the
backend paths behind those views. This item is about how fast FlatRun's own UI and API
respond, not how fast the deployed apps serve their traffic (tracked separately below).
Backend profiling (this cycle): deployment status is recomputed on every request by
shelling out to the docker compose CLI with nothing cached, so latency scales with
the number of deployments and is dominated by process startup. Listing N deployments
spawns 2N to 4N docker processes; a single detail view spawns 4 to 8. Concrete work:
Memoize compose-command detection so docker compose version runs once per process,
not once per runCompose (internal/docker/compose.go:369, :469).
Serve list/detail status from one Engine API ContainerList filtered by com.docker.compose.project labels, reusing the existing APIClient
(internal/docker/api.go:17, :33), instead of per-deployment docker compose ps.
Collapse the detail path that runs the compose sequence twice into one status read
(internal/docker/manager.go:101, :104).
Skip the status fan-out for endpoints that only need on-disk metadata (certificates,
proxy sync, traffic, cluster), reading via FindDeployments rather than ListDeployments (internal/api/server.go:4598, :5000).
Add a short-TTL cache for container status so request bursts don't each recompute it.
Record before/after list and detail timings at 1, 10, and 50 deployments.
Deployment serving performance: how fast a deployed app answers real end-user
requests through FlatRun's reverse proxy, distinct from dashboard/API speed above. The
proxy is the shared path every deployment's traffic crosses, so its defaults set the
floor for everyone. Establish a baseline and remove the obvious drags:
Measure request latency and throughput through the proxy to a deployment (p50/p95,
requests/sec) against hitting the container directly, so proxy overhead is a number.
Audit the generated nginx/openresty vhost defaults: keepalive to upstreams, gzip,
HTTP/2, sensible proxy buffer sizes, and static-asset caching headers.
Confirm no per-request work scales badly (excess logging, TLS session cache off,
DNS re-resolution of upstreams).
Document knobs an operator can turn per deployment (worker limits, cache toggles).
Integrations: deploy from a GitHub or git URL and from uploaded code, not only
from compose content or a template. Today a deployment must already have a compose
file; this adds source-based deployment with auth.
Builders: build code and then deploy it. Today only an existing compose build:
section runs. This adds build-from-source (Dockerfile detection, buildpacks / nixpacks)
with build config (args, cache, secrets).
Persist AI chat sessions: store conversation history so chats survive reloads and
can be resumed, instead of being lost per session.
Marketplace-sourced templates: the deployment flow's templates should come from the
marketplace (api.flatrun.dev) and be kept current, with flatrun/marketplace on GitHub
as the fallback source for public publications when the API is unreachable. Today the
deploy flow uses the templates embedded in the agent binary.
Firewall enforcement: the built-in firewall app persists, validates, and previews a
host-wide inbound/outbound policy, but does not enforce it yet. Translate rules to
nftables/iptables with a safeguard that preserves the active SSH session before a
default-deny inbound policy takes effect.
Security trace & false-block fix (fix: Security module blocking after 1 404 #153): stop blocking on a single 404; give every
blocked IP a trace of the events (counts, paths) that led to the block.
Effective rebuild / pull / env (bug: Rebuild, pulls etc not effective with picking new env vars #149): expose force-recreate, no-cache rebuild,
and non-cached pull so updated env vars and images actually take effect, matching what
currently requires a manual docker compose from the terminal.
UI design refresh: improve the UI design and adopt Iconify for iconography. First pass shipped in feat(ui): Design refresh with dark mode, assistant, and global search ui#74 (dark mode, design tokens, Iconify, the assistant rework, global search). Per-screen polish and modal consolidation still to follow.
Seed bind mounts from image content (Seed bind mounts from image content when host path is empty #139): opt-in per-mount seeding so an empty or
missing host path is populated from the image (mirroring named-volume copy-on-first-use)
before compose up, only when the target is empty. Fixes single-file config mounts
becoming directories and empty mounts for images that don't self-populate.
User-set primary service (enh: Smarter primary service detection in multi-service compose files #111): the smarter fallback shipped (detection now prefers app/web before the first service with ports, instead of random map order). Letting the
user explicitly set a deployment's primary service is still open.
Compose validation working directory (fix(compose): Resolve relative env_file against the deployment directory #161): validation hardcodes the working
directory to ., so a relative env_file (e.g. ./.env) fails on the image-set /
update path even though the file exists in the deployment directory. Resolve relative
paths against the deployment directory, as the up path already does.
Certificate marked for auto renewal not auto-renewed (fix: Auto-renew certificates by default, with honest renew reporting and proxy tuning #167): auto-renewal
was off by default and, even when on, the global flag could veto a certificate the user had
marked for renewal. Auto-renewal now defaults on, the renewer runs whenever certbot is
enabled, and each certificate's own setting wins over the global default. Single Renew also
stops reporting success when nothing was reissued, with force as an explicit option. Serving
performance was tuned in the same PR (HTTP/1.1 to upstreams on all routes, buffered access
logging, wider gzip, larger proxy buffers).
Routing-only hostnames for externally-fronted proxies (feat(nginx): Support routing-only hostnames with externally-terminated TLS #166): a hostname fronted by
an external proxy (e.g. dashboard.app.com on Cloudflare proxying to a deployment on api.app.com) arrives with an unmatched Host and is rejected instead of routed, and it
cannot be added as an alias because aliases force certificate issuance that fails when the
DNS points at the external proxy. Needs an opt-in route-only hostname whose TLS is
terminated upstream. Root cause and checklist in feat(nginx): Support routing-only hostnames with externally-terminated TLS #166.
Observability follow-ups (from feat: Add AI-native observability and self-recovery module #148 review, non-blocking): (a) the health watcher
holds its lock across the docker restart call, briefly blocking health reads during a
restart; (b) notification target URLs, including SMTP passwords, are stored in cleartext
in the runtime config, consistent with the existing flat-file secret handling; (c) the
notification HTTP handlers (including the plugin-token gate on emit) lack boundary tests;
(d) set_auto_restart is a global toggle but requires a deployment-scoped session, so it
fails in an unscoped assistant session.
Housekeeping
Document the release naming system: keep the convention below current and reuse
it when opening the next umbrella issue.
Release naming: umbrella releases are codenamed after sea creatures, advancing one
letter per release. Albacore (A) -> Barnacle (B) -> Cuttlefish (C) -> Dolphin (D) ...
Pick the next unused letter when opening each new group(enhancements): issue.
Albacore release. Theme: full deployment lifecycle and self-healing operations.
Umbrella for the next release; each item below ships this cycle.
Features
docs/BACKUPS.md; object-storage secrets live in the credential manager, never the flat-file config. This also seeds the object store abstraction (external vs managed stores, a MinIO template so FlatRun can run its own S3 endpoint), specced indocs/OBJECT_STORES.md. Follow-up slices, still open: one-click managed deploy with auto-register, an object browser, deployment consumption, and store-to-store replication.list and dashboard instead of loading everything at once; profile and optimize the
backend paths behind those views. This item is about how fast FlatRun's own UI and API
respond, not how fast the deployed apps serve their traffic (tracked separately below).
Backend profiling (this cycle): deployment status is recomputed on every request by
shelling out to the
docker composeCLI with nothing cached, so latency scales withthe number of deployments and is dominated by process startup. Listing N deployments
spawns 2N to 4N
dockerprocesses; a single detail view spawns 4 to 8. Concrete work:docker compose versionruns once per process,not once per
runCompose(internal/docker/compose.go:369,:469).ContainerListfiltered bycom.docker.compose.projectlabels, reusing the existingAPIClient(
internal/docker/api.go:17,:33), instead of per-deploymentdocker compose ps.(
internal/docker/manager.go:101,:104).proxy sync, traffic, cluster), reading via
FindDeploymentsrather thanListDeployments(internal/api/server.go:4598,:5000).requests through FlatRun's reverse proxy, distinct from dashboard/API speed above. The
proxy is the shared path every deployment's traffic crosses, so its defaults set the
floor for everyone. Establish a baseline and remove the obvious drags:
requests/sec) against hitting the container directly, so proxy overhead is a number.
HTTP/2, sensible proxy buffer sizes, and static-asset caching headers.
DNS re-resolution of upstreams).
visible next to its CPU and memory, closing the loop with feat: Add AI-native observability and self-recovery module #148.
from compose content or a template. Today a deployment must already have a compose
file; this adds source-based deployment with auth.
build:section runs. This adds build-from-source (Dockerfile detection, buildpacks / nixpacks)
with build config (args, cache, secrets).
summarization, and related tools (UI side: feat(ai): Add AI workflow to file viewers/editors accross the application ui#71).
can be resumed, instead of being lost per session.
marketplace (api.flatrun.dev) and be kept current, with
flatrun/marketplaceon GitHubas the fallback source for public publications when the API is unreachable. Today the
deploy flow uses the templates embedded in the agent binary.
host-wide inbound/outbound policy, but does not enforce it yet. Translate rules to
nftables/iptables with a safeguard that preserves the active SSH session before a
default-deny inbound policy takes effect.
Improvements
nginx vhost generation (nginx vhost generation: WebSocket timeouts, unconditional upgrade header, ssl_stapling noise, missing target validation #156): configurable / long WebSocket proxy timeouts;
conditional
Connection: upgradevia amapinstead of unconditional; skipssl_staplingwhen the cert has no OCSP responder; validate the target service/portactually listens before saving a vhost.
Streamed deployment actions (feat: Stream start/stop progress and run deployment actions as jobs #150): run start/stop/restart as background jobs that
return a job id; job status and buffered output survive a page reload; stream compose
output over the WebSocket keyed by job id with a poll fallback; serialize concurrent
actions per deployment. Shipped in feat(agent): Run deployment and service actions as streamed jobs #163 and feat(ui): Stream deployment and service action progress ui#76. (Jobs are
in-memory with ~15-minute retention: reload survives, an agent restart does not.)
Security trace & false-block fix (fix: Security module blocking after 1 404 #153): stop blocking on a single 404; give every
blocked IP a trace of the events (counts, paths) that led to the block.
Effective rebuild / pull / env (bug: Rebuild, pulls etc not effective with picking new env vars #149): expose force-recreate, no-cache rebuild,
and non-cached pull so updated env vars and images actually take effect, matching what
currently requires a manual
docker composefrom the terminal.UI design refresh: improve the UI design and adopt Iconify for iconography. First pass shipped in feat(ui): Design refresh with dark mode, assistant, and global search ui#74 (dark mode, design tokens, Iconify, the assistant rework, global search). Per-screen polish and modal consolidation still to follow.
Seed bind mounts from image content (Seed bind mounts from image content when host path is empty #139): opt-in per-mount seeding so an empty or
missing host path is populated from the image (mirroring named-volume copy-on-first-use)
before compose up, only when the target is empty. Fixes single-file config mounts
becoming directories and empty mounts for images that don't self-populate.
nginx additional-domain upstream collision (fix(nginx): Multi-domain upstream collides across deployments; server_names_hash not configurable #155): the extra-domain path proxies by
bare service name (e.g.
app), which is not unique across deployments, so a customdomain on a shared host intermittently serves another deployment's container. Route by
the unique deployment name like the primary-domain path.
nginx security base image (bug: Use flatrun/openresty for security base #106, check: Ensure base nginx image is configured to flatrun/openresty #39): use
flatrun/openrestyas the nginx baseinstead of upstream
openresty/openresty, which is incompatible with the FlatRunsecurity configs.
User-set primary service (enh: Smarter primary service detection in multi-service compose files #111): the smarter fallback shipped (detection now prefers
app/webbefore the first service with ports, instead of random map order). Letting theuser explicitly set a deployment's primary service is still open.
Propagate metadata-save errors (enh: Propagate SaveMetadata errors during compose updates #110): surface
SaveMetadatafailures during composeupdates instead of discarding them, so compose and
service.ymlcan't silently diverge.Suppress ACME challenge log noise (enh: Suppress logging for ACME challenge requests in nginx configs #112): set
access_log off/log_not_found offon the well-known ACME challenge locations so cleaned-up challenge 404s stop burying real
errors. Complements the fix: Security module blocking after 1 404 #153 security trace.
--helplists subcommands (enh:--helpcommand flag omits subcommands (update, setup, version) #130): top-level help should listupdate,setup, andversion, andhelpshould print usage instead of falling through to start the server.Compose validation working directory (fix(compose): Resolve relative env_file against the deployment directory #161): validation hardcodes the working
directory to
., so a relativeenv_file(e.g../.env) fails on the image-set /update path even though the file exists in the deployment directory. Resolve relative
paths against the deployment directory, as the
uppath already does.Certificate marked for auto renewal not auto-renewed (fix: Auto-renew certificates by default, with honest renew reporting and proxy tuning #167): auto-renewal
was off by default and, even when on, the global flag could veto a certificate the user had
marked for renewal. Auto-renewal now defaults on, the renewer runs whenever certbot is
enabled, and each certificate's own setting wins over the global default. Single Renew also
stops reporting success when nothing was reissued, with force as an explicit option. Serving
performance was tuned in the same PR (HTTP/1.1 to upstreams on all routes, buffered access
logging, wider gzip, larger proxy buffers).
Routing-only hostnames for externally-fronted proxies (feat(nginx): Support routing-only hostnames with externally-terminated TLS #166): a hostname fronted by
an external proxy (e.g.
dashboard.app.comon Cloudflare proxying to a deployment onapi.app.com) arrives with an unmatched Host and is rejected instead of routed, and itcannot be added as an alias because aliases force certificate issuance that fails when the
DNS points at the external proxy. Needs an opt-in route-only hostname whose TLS is
terminated upstream. Root cause and checklist in feat(nginx): Support routing-only hostnames with externally-terminated TLS #166.
Observability follow-ups (from feat: Add AI-native observability and self-recovery module #148 review, non-blocking): (a) the health watcher
holds its lock across the docker restart call, briefly blocking health reads during a
restart; (b) notification target URLs, including SMTP passwords, are stored in cleartext
in the runtime config, consistent with the existing flat-file secret handling; (c) the
notification HTTP handlers (including the plugin-token gate on emit) lack boundary tests;
(d)
set_auto_restartis a global toggle but requires a deployment-scoped session, so itfails in an unscoped assistant session.
Housekeeping
it when opening the next umbrella issue.
Release naming: umbrella releases are codenamed after sea creatures, advancing one
letter per release. Albacore (A) -> Barnacle (B) -> Cuttlefish (C) -> Dolphin (D) ...
Pick the next unused letter when opening each new
group(enhancements):issue.