Project
Roadmap
This is the planned order of work, released and to come. It changes as we learn; the issue tracker is the live view.
Every release keeps the stability promise: apps written for v1.0 keep working, and new features arrive as additions you opt into. A release ships with its docs, tests and an example of the feature in use.
The plan, at a glance:
| Version | Theme |
|---|---|
| v1.9 | Logging, security signals, tern deploy, tern.land (released) |
| v1.10 | Production confidence: tracing, error reports, test helpers (released) |
| v1.11 | Teams and billing, for SaaS apps (released) |
| v1.12 | Rendering on demand, and caching (released) |
| v1.13 | Native rendering, phase 3: no JavaScript engine for most apps (released) |
| Later | Search, feature flags, admin screens, AI, editor support, hosting |
#v0.1: Foundations (client-rendered)
The goal: you can build a real app with Tern and deploy it as one binary.
- Route scanner and matcher (
slug.param,lang.optional,path.rest,app.group,id.param.matcher) - Manifest code generation for route packages
-
kit:Ctx,Load, layouts withParent(),Redirect/Error/NotFound - Form actions with
Failand progressive enhancement - Endpoints (
GET,POST, … in a route's Go package) - Hooks (
kit.WithHook) andnet/httpmiddleware - Go → TypeScript type generation, including layout data merging
- Client runtime: router,
goto,invalidateAll,pageandnavigatingstate,use:enhance, preloading - Vite plugin and asset manifest
-
tern dev: single port, Go restart, Vite HMR, error overlay -
tern build: embedded single binary -
tern newwith a starter template - Example apps: the
tern newstarter andexamples/todos, with end-to-end tests - Documentation: guide, deployment, architecture
#v0.2: Server-side rendering
- Embedded JavaScript engine for Svelte SSR (
modernc.org/quickjs, no cgo; see ADR 0004) - Hydration instead of client-only mounting
- Renderer pool with timeouts, memory limits and fallback to client rendering
- Server rendering in development through Vite
- Per-route
const SSR = falseand app-widekit.WithoutSSR()
#v0.3: Performance and deployment
- Streaming deferred data from loads (
kit.Defer, rendered with{#await}) - Prerendering at build time (
const Prerender = true,Entriesfor dynamic routes) - Deployment guide: Docker, systemd, Fly.io, reverse proxies (docs/deployment.md)
- Assets: Vite imports (
import logo from '$lib/logo.svg') andstatic/, with immutable caching
#v1.0: Stable
- Plugin system and built-in plugins (health, request logging, compression, security headers)
- Base paths (
tern.json) - Public environment variables
- Stability promise (stability.md)
#v1.1
- More plugins: sessions, metrics, rate limiting, CORS
- Service workers
- Translations (
src/locales,t(),ctx.T) - Responsive images (
?image,<Image>)
#v1.2
- Server rendering: memory leak fixed, worker cap, render cache (ADR 0007)
- Web APIs for libraries during server rendering, and a compatibility suite checked against Node
- Fuzz tests for routing and messages
- Markdown pages and content collections
- WebSocket and server-sent event helpers, with pub/sub
- Background jobs, cron and database migrations
- OAuth2 and OpenID Connect: sign-in with providers, a provider of your own, and roles (ADR 0008)
- Email with Svelte templates; file uploads and storage
- OpenAPI from endpoints, with a typed client
- A status page example
- Experimental:
<script lang="go">, in WebAssembly and live on the server (notes) - Remote images for
<Image>, translated URL paths, starter templates, a SvelteKit migration guide
#v1.3
- Live components, production ready: per-component goroutines, typed markup with
tern check, patches, pending and optimistic updates, bindings, events, several server processes through a broker - An MCP server covering all of Tern: project scaffolding, routes and their types, docs, codegen, and running checks, for AI coding tools
- A component library: accessible, keyboard-friendly Svelte components with Tern's own design, themeable with CSS variables
#v1.4: Everything an app needs (released)
Built alongside a real app, a self-hosted web analytics tool, so every piece is used before it ships.
Starting a project
-
npm create tern@latest, and an interactivetern new: name, template, Tern UI, git, install -
ctx.ClientIP()with trusted proxies, the Intl shim'ssignDisplay, a password sign-in recipe
Tern UI: the complete set
- Overlays: Popover, Hover Card, Alert Dialog, Sheet, Drawer, Context Menu, Menubar
- Inputs: Combobox, Command palette, custom Select, Slider, Toggle, Toggle Group, Input OTP, Input Group, Date Picker, Calendar, Label, Button Group, Password input
- Layout and navigation: Sidebar, Navigation Menu, Breadcrumb, Pagination, Collapsible, Resizable, Scroll Area, Aspect Ratio, Carousel, Empty, Item, Table
- Data: a Data Table with sorting, filtering, selection and pagination
- Charts: line, area, bar and donut, with axes, tooltips, legends, and a table for screen readers
- Blocks wired to Tern's plugins: sign-in and sign-up (passwords,
plugins/oauth), the consent screen forplugins/oidc, and an app shell (Sidebar) -
tern ui add <component>: copy a component into the project, to own and change it
Validation
- Schemas in Go that validate actions and endpoints, generate matching TypeScript validators so the page checks as you type, and show each error on its
Field
The analytics app
- A tracking script and collection endpoint, rollups as jobs, retention, a live dashboard, weekly email reports, a stats API with OpenAPI, one binary
#v1.5: Native rendering (released)
benchmarks/vs-sveltekit showed renders 1.8× to 8.5× slower than SvelteKit on Node, from Svelte's code running in QuickJS. ADR 0011 has the design and how it turned out.
- Profile a render, and a hand-written spike with the same HTML as Svelte
- A compiler from the server build's modules (components, Tern's and Svelte's runtimes, libraries) to Go, with what it can't compile left to the JavaScript engine
- A Go model of JavaScript values with QuickJS's semantics: shapes and inline caches, realms that render in parallel, generators, typed arrays, Proxy; conformance tests against Node
- Intrinsics for Svelte's hottest helpers, used only while their source is unchanged
-
tern buildcompiling it and reporting what isn't native; fallback to the engine for anything that fails - Every example rendering natively with the engine's HTML, checked with
TERN_NATIVE_VERIFY=1 - Typed fast paths from the loads' Go structs (phase 2)
- Smaller binaries for large apps, and the vs-sveltekit benchmark tracked in CI: function descriptors as static data, no server bundle without the engine (dashboard 38.9 → 37.4 MB); each run on
maincompared with the last, with a warning when Tern is 10% slower or its binary 5% larger
#v1.6: Fully native rendering, and accounts (released)
Native rendering, complete
- Compile what was left to the JavaScript engine: dynamic
import(), BigInt, async generators andfor await,new.target - Every built-in QuickJS has: grouping, Set methods, iterator helpers,
WeakRef, the RegExpdandvflags, and the rest - A page's own errors keep it native; only what native rendering can't do sends a page to the engine
- Less work per render: the payload says when it has deferred values, and the result isn't encoded as JSON
Translations for everything built in
- Tern UI's texts ("Show password", "Previous page", …) and the validation messages through
src/localesandt(), in Go and in the browser, with English as the default
Type rough edges
- Go slices that are never nil, typed without
| null:nilsent as[], or a tag to say so - Controls that post through hidden inputs, such as
Combobox, checked as the user types inside aForm
Accounts
- Password reset and email verification, with email and jobs
- Passkeys (WebAuthn): sign-in without a password
- Signed-in devices: a list of sessions, and signing out of the others
- Tern UI screens for each, and examples/accounts
#v1.7: Faster native rendering, and accounts for real apps (released)
Native rendering
- Page data encoded by Tern's own JSON encoder, without encoding/json or a copy for nil slices: 6× faster, uncached renders 17% faster
- Smaller binaries for large apps: 11% from compiling the renderer without inlining, at the same speed
- The vs-sveltekit benchmark run in CI, and every change's renders compared with the commit before
Accounts
- Two-step sign-in with an authenticator app (TOTP), and recovery codes
- Sign in with GitHub, Google or OpenID Connect into an accounts user
- Changing the email address, confirmed through the new one
- Sign-in, sign-up and reset attempts limited per address and per IP, built in
CLI
-
tern routes -json -
tern devsays which routes were added and removed
#v1.8: A full app to start from, and native rendering phase 2 (released)
Starting a project
-
tern newasks for a bare app or a full one: thesaastemplate, with accounts, a dashboard, settings and per-user data -
tern devreads.env
Native rendering, phase 2 (ADR 0013)
- Page data handed to the renderer as Go values, without parsing JSON in the realm
- Svelte's Renderer output and its attribute helpers in Go, checked against Node
- The document written in one buffer
- Typed fast paths from the loads' Go structs: profiled and set aside, as inline caches already make property reads cheap
#v1.9: Logging, security signals, and deploying (released)
Logging (ADR 0014)
- Structured logging on
log/slogwith request IDs, trace context, redaction and sampling - Sinks for OpenTelemetry (OTLP), Loki, Datadog and rotating files
- A live log view in
tern dev
HTTPS and security signals (ADR 0015)
- HTTPS served by the app itself, with certificates from Let's Encrypt
-
plugins/signals: JA4, HTTP/2 and header-order fingerprints, a score with reasons, a client check with a proof of work, device IDs - Rate limits per client fingerprint, and emails about sign-ins from new devices
Deploying (ADR 0016)
-
tern deployto a server over SSH, with zero-downtime restarts, releases, rollbacks and logs - To Fly.io, and to container registries with the platform's deploy command after
- Providers of your own, as
tern-deploy-<name>programs
tern.land
- The website rebuilt as a Tern app, every page prerendered: a landing page, the docs with search, the decision records and the examples
- A styled first paint under
tern dev
#v1.10: Production confidence (released)
Knowing what an app does in production, and that it still does it after a change.
Tracing (ADR 0017)
- OpenTelemetry spans for each request, and the loads, actions, renders, SQL queries, jobs and outgoing HTTP calls in it, exported over OTLP next to the logs
- A waterfall of each request in
tern dev's log view - Trace context passed on to outgoing requests and jobs, so a trace follows work across services
Error reports (ADR 0018)
- Unexpected errors and panics grouped by cause, with stack traces, the request and the user, and sent to Sentry-compatible services
- Browser errors reported to the server the same way
- A list in
tern dev's log view, as they happen
Testing (docs/testing.md)
-
kit/kittest: call loads, actions and endpoints from Go tests, as a signed-in user, with a test database that resets - Render a page in a test and query its HTML
- Browser tests in the templates, with Playwright set up and an example test
#v1.11: Teams and billing (released)
What the SaaS template still leaves to you.
Teams
- Organizations in
accounts: members, invitations by email, roles withrbac, and switching between teams - An audit log of account and team events, for users and for support
- Tern UI screens for team settings, members and invitations
Billing
- A billing plugin for Stripe: Checkout, the customer portal, plans and subscriptions, trials, and seats per team
- Webhooks verified and processed as jobs, so a slow or repeated event is safe
- Limits per plan, checked in loads and actions
- Pricing and billing screens in Tern UI, and both in the saas template
#v1.12: Rendering on demand and caching (released)
Pages as fast as prerendered ones, with data that changes.
- Incremental prerendering: a page is rendered on its first request, served from the cache after, and rendered again after a time or when its data changes
- Tags on loads and pages, and
kit.Revalidate(tag)to refresh everything that used some data - A cache for load results, in memory, on disk or in Redis, shared by every process
- Cache headers per route, for CDNs in front of the app
- Pages with nothing interactive can ship no JavaScript at all
#v1.13: Native rendering, phase 3 (released)
Every page rendered by Go, and smaller binaries.
A value representation without boxing numbers and strings: profiled and decided against, as it would gain about a tenth for a rewrite of the runtime (ADR 0022)-
tern buildleaves the JavaScript engine out of apps that don't need it, for smaller binaries - Native rendering for emails and live components too
#v2.0: Leaner native rendering (planned)
Faster pages that can't come from a cache, smaller binaries and less memory, each change measured with benchmarks/compare.sh and the Tern vs SvelteKit run in CI.
Uncached renders (/items takes about 250 µs, most of it calls between compiled functions and allocating values)
- Reuse the arguments slices of calls, where the callee doesn't keep them (
arguments, rest parameters andbindcopy first) - Direct calls between compiled functions when the callee is known at compile time, without the generic call protocol
- Revisit unboxed values (ADR 0022) only with a profile from a real app that shows boxing as the main cost
Binary size (the dashboard example: 37.4 MB, 11 MB of it the compiled renderer)
- Generated code that shares more: common call and property-access sequences as runtime helpers instead of inline code
- Static assets kept only compressed, decompressed for the rare client that doesn't accept gzip
Memory
- Less memory on data-heavy pages (about 110 MB resident for the items benchmark, with four workers): a heap profile of where it goes, and the biggest uses cut
#v1.16: Secure by default, and typed SQL (released)
- Security headers, a Content-Security-Policy and a rate limit on writes, on by default (docs/security.md)
- Known vulnerabilities in Go modules and npm packages, in
tern doctorand CI - Routes closed by default: each route with server code says who may use it (ADR 0027)
- Typed SQL from
queries/*.sql, checked against the schema (ADR 0028) - Checks for risky migrations:
tern migrate check, and intern doctor - An HTTP client for outgoing requests that refuses internal addresses (
safehttp) - Settings and secrets declared once, checked at start and kept out of logs (
settings) - Encrypted fields for personal data (
sealed) - Safer uploads: photo metadata removed, private files unless
Public: true
#Later
Planned, in no fixed order yet. Tell us in the issues which you need first.
Building
- Fewer allocations in native renders: boxes for HTML and page data from slabs, 40% fewer allocations on the 100-row table (ADR 0022)
- A
tern buildthat needs only Go, with Svelte's compiler compiled to Go and esbuild for bundling (ADR 0026) - CommonJS modules in native rendering
-
fn.toString()with the bundle's text, so verify mode finds no difference at all - Full-text search for content collections and app data, on SQLite or PostgreSQL, with a search box in Tern UI (docs/search.md)
- Feature flags and gradual rollouts, decided on the server, per user or team (docs/flags.md)
- Dev tools in
tern dev: a panel with each page's route, files, data, spans and logs, and errors over the page with their source (docs/dev-tools.md) -
tern doctor: checks before a deploy, of tools, versions, code generation, native rendering, migrations and the target's settings (docs/deployment.md) - Operations screens in the admin panel: users, the audit log, jobs with Retry, and error reports; actions on records (docs/admin.md)
- Admin screens generated from Go structs: lists, filters and forms for an app's own data (docs/admin.md)
- Presence and shared state for live components, for collaborative screens (docs/live-components.md)
AI
- Streaming endpoints for model responses, with typed partial results on the client (guide)
- An app's tools served over MCP, so assistants can use the app with the user's permissions (docs/mcp-server.md)
Tooling
- An editor extension: jump between a page and its Go load, see routes, and get
tern checkerrors on save, for VS Code and Zed (docs/editors.md) -
tern addfor plugins, and a list of community plugins on tern.land (tern.land/plugins)
Hosting
- Hosting platforms as
tern deployproviders, with what they add: previews per branch, logs and traces in one place, and managed databases. Tern stays deployable anywhere; no feature depends on a platform
Learning
- A tutorial on tern.land that builds a complete app, one step per page
- A showcase of apps built with Tern