Content Lifecycle
Most frameworks leave content visibility to the application developer — a forgotten WHERE status = 'published' clause and draft content leaks to public endpoints, sitemaps, and search indexes. Forge makes the lifecycle a framework-level guarantee. You define the states; Forge enforces them everywhere, automatically.
The four states
forge.Draft // not publicly visible
forge.Scheduled // publishes automatically at ScheduledAt
forge.Published // publicly visible
forge.Archived // hidden, preserved in storage
The lifecycle is enforced by Forge at the framework level. You cannot opt out of it. This is intentional — it is what guarantees that draft content never leaks to public endpoints, sitemaps, feeds, or AI indexes.
What Forge enforces automatically
| Status | Public GET | Sitemap | RSS | AIDoc | llms.txt |
|---|---|---|---|---|---|
| Draft | 404 | ✗ | ✗ | 404 | ✗ |
| Scheduled | 404 | ✗ | ✗ | 404 | ✗ |
| Published | ✓ | ✓ | ✓ | ✓ | ✓ |
| Archived | 404 | ✗ | ✗ | 404 | ✗ |
Non-published content returns 404 — not 403. Forge does not leak the existence of draft or scheduled content.
Scheduled publishing
Set Status to forge.Scheduled and provide a ScheduledAt time:
schedAt := time.Now().Add(24 * time.Hour)
post := &Post{
Node: forge.Node{
Slug: "my-post",
Status: forge.Scheduled,
ScheduledAt: &schedAt,
},
Title: "My Post",
}
Forge runs an internal ticker. No external cron job required. When ScheduledAt arrives, the item transitions to Published and the AfterPublish signal fires — triggering sitemap regeneration, feed regeneration, and any custom hooks you have registered.
Signals
React to lifecycle events with forge.On:
forge.On(forge.AfterPublish, func(ctx forge.Context, p *Post) error {
log.Printf("published: %s", p.Slug)
return nil
})
Available signals: BeforeCreate, AfterCreate, BeforeUpdate, AfterUpdate, AfterPublish, SitemapRegenerate.
Why lifecycle is first-class
Content systems that treat visibility as an application concern invite mistakes. A new route, a new feed, a new AI endpoint — each one requires the developer to remember to filter by status. One oversight and draft content is public.
Forge takes a different approach: the lifecycle is the framework's responsibility, not the application's. Every public endpoint, sitemap entry, RSS item, and MCP resource is filtered at the framework level. A content type that embeds forge.Node gets this guarantee for free — there is no way to accidentally expose a draft.
This is why Forge describes content lifecycle as a first-class concept rather than a convention.