Redirects an agent can manage while the server runs

Renaming a URL used to mean a 404 and lost inbound links. Smeldr v1.34.0 adds redirects as a first-class concept: a compile-time option for known renames, and a runtime, database-backed table an AI agent or operator manages live over MCP — 301 or 410, exact or whole-prefix, with redirect chains auto-collapsed. Zero overhead on requests that match a real route.

The problem

Content moves. A post is renamed, a docs page is reorganised, a whole section changes its URL prefix. Until v1.34.0 Smeldr had no answer for that: the old URL simply returned a 404, every inbound link and every search result pointing at it went dead, and there was no way to fix it short of editing Go code and redeploying.

That last part matters most. Smeldr's whole premise is that an AI agent or an operator runs the site over MCP — no code, no deploy. A redirect mechanism that required a redeploy would be a hole in exactly the surface that is supposed to be agent-operable.

So redirects landed in two layers: one for renames you know about at build time, and one an agent or operator manages live while the server runs.

Layer one: redirects in code

For renames you control in the source, two additive APIs cover it.

A single manual rule:

app.Redirect("/old-path", "/new-path", smeldr.Permanent) // 301
app.Redirect("/removed", "", smeldr.Gone)                // 410, empty destination

smeldr.Permanent is a 301 (the change is final; search engines should follow it and update). smeldr.Gone is a 410 — the resource is intentionally gone, and 410 de-indexes far faster than a 404 ever will.

And when you rename a module's entire prefix, one option preserves every link under it:

app.Content(&BlogPost{},
    smeldr.At("/articles"),
    smeldr.Redirects(smeldr.From("/posts"), "/articles"),
)

/posts/hello now 301s to /articles/hello — the suffix is rewritten onto the new prefix at request time, so a single rule covers the whole tree.

Layer two: redirects the agent manages

The interesting layer is the runtime one. Activate database-backed management with one call:

app.Redirects(db) // creates the smeldr_redirects table, loads existing rules

That does three things: it ensures the smeldr_redirects table exists, loads any saved rules into the in-memory store, and — because the DB is now wired — enables three MCP tools (Editor role):

ToolWhat it does
create_redirectcreate or upsert a rule: from (required), to, code, is_prefix
list_redirectslist every rule, code-registered and database-saved
delete_redirectremove a rule by its from path

So an agent asked to "rename /pricing to /plans and keep the old link working" makes one create_redirect call and it is live immediately — no restart, no redeploy. The same three operations exist on the CLI for the weekend when the AI is offline:

smeldr-cli redirect create --from /pricing --to /plans   # 301
smeldr-cli redirect create --from /gone --code 410        # 410 Gone
smeldr-cli redirect create --from /posts --to /articles --prefix
smeldr-cli redirect list
smeldr-cli redirect delete /pricing

The details that make it safe

Redirect chains collapse. Rename A→B today, then B→C next month, and a naive table leaves a visitor bouncing A→B→C. Smeldr collapses on insert: adding B→C rewrites the stored A→B into A→C automatically, so every request is a single hop. Chains deeper than ten levels panic rather than silently misbehave, and a Gone destination is terminal — chains never collapse through a 410.

Prefix rules are longest-match-first. Exact matches are an O(1) map lookup; prefix rules are scanned longest-first so the most specific rule wins.

Zero overhead on real routes. The redirect store is a fallback handler mounted at /. It is only ever reached for a request that matched no real route — a served page pays nothing for the redirect table existing.

There is a manifest. /.well-known/redirects.json exposes the full rule set (guard it with app.RedirectManifestAuth(...)), so the redirect map is itself inspectable rather than buried in server state.

Why it fits Smeldr

Redirects are exactly the kind of operational task that used to demand a developer and a deploy, and now doesn't. The rules live in the database, an agent edits them in plain language over MCP, the CLI is the human fallback, and the changes are live the instant they are made. The framework stays headless and the operation moves to where everything else in Smeldr already is: the agent.

app.Redirects(db) — and the 404s stop being permanent.