Three things need to work before a new Forge instance is usable: the database comes up, the first admin token exists, and static files are served. Until this week, two of those three were your problem.
The bootstrap token
The old way: run a helper script, create a token manually, copy it somewhere safe. One misstep and you were locked out before you started.
The new way: on first start, if forge_tokens is empty, Forge creates a bootstrap-admin token automatically. It logs the raw token value once — via slog.Warn, visible in startup output, never stored anywhere else. From that point you own the token management.
No script. No chicken-and-egg. One line in your logs.
forge-cli init
Once you have that bootstrap token, forge-cli init takes it from there:
forge-cli init --url https://yoursite.com --bootstrap-token <token>
It validates reachability via /_health, creates a named admin token, writes .forge-cli.env with FORGE_URL and FORGE_TOKEN, then verifies the new token works. Your CLI is configured. The bootstrap token stays as a break-glass backup.
Static files: dev and production
Static file serving had a gap. In development you want files read from disk — edit a CSS file, reload, see the change. In production you want an embedded fs.FS with immutable Cache-Control headers.
App.Static now handles both in one call:
//go:embed static
var staticFS embed.FS
app.Static("/static/", staticFS, "static")
Config.Dev controls which mode is active. Set it from your config file: dev = true. No build pipeline. No separate dev server.
forge-media v1.1.2
A path traversal fix in LocalMediaStore. A crafted filename like ../../etc/passwd previously escaped the media root. Fixed by replacing filepath.Join with os.Root (Go 1.24+), which prevents traversal at the OS level regardless of input (CWE-22).
If you use forge-media, upgrade to v1.1.2. Changelog and release at github.com/forge-cms/forge-media.
forge-cms.dev — github.com/forge-cms/forge
-- @ravnthers