Releases: dev2k6/Nguyen.go
v1.4.0
v1.4.0 — .live.gox Codegen, Effect Wall, Reattach Wire-up
Released: 2026-05-17
v1.4.0 closes the gaps between v1.3.0's release notes and its actual
implementation, ships the .live.gox codegen that was the primary
v1.3.1 roadmap item, and adds the first Effect Wall rules to
nguyen vet.
Highlights
.live.gox codegen — zero-boilerplate Live pages
Write a Live page as a single file. No RegisterLivePage call, no
separate Go struct file:
---
var Count int
func inc(state *CounterState) { state.Count++ }
func dec(state *CounterState) { state.Count-- }
---
<section>
<h1>Count: {Count}</h1>
<button @click="dec()">-</button>
<button @click="inc()">+</button>
</section>
Save as pages/counter.live.gox. The compiler:
- Detects
File.IsLive(.live.goxsuffix or//+nguyen:livedirective) - Calls
parser.TranspileLive— generates alivepage.Pageimplementation - Writes the Go source to
<buildDir>/live/<name>.go - Skips TinyGo entirely — no WASM compilation for live pages
The server scans <buildDir>/live/ on startup and registers each page
automatically.
Session reattach — wired end-to-end
Hub.Reattach was implemented in v1.3.0 but never called by the
WebSocket handler. v1.4.0 completes the wire-up:
readLoopnow checksevt.Kind == "reattach"before dispatching- On valid token: swaps the session reference, logs
live.session.reattach - On expired token: sends
{t:"error"}and closes the connection tokenIndexis kept alive aftersess.Close()so reconnects within
IdleTimeoutalways find the session
Live stats in /_nguyen/health
{
"status": "ok",
"version": "1.4.0",
"live": {
"sessions": 42,
"dropped_messages": 0
}
}HealthHandler now accepts a *live.Hub and exposes session count
and cumulative dropped message count.
WithHubOptions on pkg/nguyen.App
Hub tuning is now fully exposed:
app := nguyen.New(
nguyen.WithHubOptions(live.Options{
MaxSessions: 50_000,
IdleTimeout: 90 * time.Second,
OutboundBuffer: 32,
}),
)Previously live.NewHub was always called with empty options.
Effect Wall — first live-aware vet rules
nguyen vet gains two new rules for .live.gox files:
live-render-pure — flags DB, HTTP, and FS calls inside Render
methods. These belong in Init or Handle:
pages/dashboard.live.gox:14: error [live-render-pure]
Render calls "db." — move side effects to Init or Handle
live-handler-returns-state — flags handler functions bound via
@event that have no return statement. Live handlers must return
the (possibly mutated) state:
pages/counter.live.gox:8: error [live-handler-returns-state]
handler "inc" in .live.gox has no return statement — it must return the new state
These are the first rules that use go/ast to inspect .live.gox
frontmatter with live-mode awareness, forming the foundation of the
Effect Wall.
Bug fixes / correctness
tokenIndexis no longer cleared onsess.Close()— only on reap
or successful reattach. This was the root cause of reattach always
returningErrTokenExpiredin v1.3.0.TestReattachandTestDroppedMessagesCounteradded to
internal/live/live_test.go.
Compatibility
Fully backward compatible with v1.3.x.
New public API:
nguyen.WithHubOptions(live.Options)— new option functionserver.HealthHandler(*live.Hub)— signature change (nil-safe; pass
nilfor the no-hub case used byinternal/server/fiber_app.go)parser.TranspileLive(*File) *LiveComponentInfo— new functionparser.LiveComponentInfo— new typevet.DefaultRules()— two new rules added
What's next
v1.5 will land:
- Telegram Protocol — typed streaming wire format for SSR with
out-of-order rendering and per-frame error recovery - Parallel routes — render multiple independent route segments
simultaneously (Next.js@slotpattern) - Spore — component-as-process model with bounded mailboxes and
supervision trees
Install
go install github.com/dev2k6/Nguyen.go/cmd/nguyen@v1.4.0
go get github.com/dev2k6/Nguyen.go@v1.4.0Author
Thái Nguyên thainguyen.junior@gmail.com
v1.3.0
v1.3.0 — Keyed Diff, Session Reattach, Bug Fixes
Released: 2026-05-16
v1.3.0 ships the Live Mode improvements roadmapped in v1.2.0, fixes
four correctness bugs that could silently break generated code, and
hardens several security and reliability edges found during codebase
review.
Highlights
Keyed reconciliation in the server-side differ
The Live Mode VDOM differ now performs O(n) keyed reconciliation when
children carry a data-key attribute. Previously any change in child
count caused a full replace of the parent element. Now only the
changed nodes are patched:
<ul>
<li data-key="1">Alice</li>
<li data-key="2">Bob</li>
</ul>Adding, removing or reordering keyed children emits minimal insert
and remove ops instead of replacing the whole list. Unkeyed lists
fall back to the previous behaviour — fully backward compatible.
Persistent session reattach
Live Mode sessions now survive WebSocket reconnects. On first connect
the server issues a 32-hex-char reattach token:
{"t":"session","token":"a3f8..."}The browser stores the token and sends it on reconnect:
{"t":"reattach","token":"a3f8..."}Hub.Reattach(ctx, token) looks up the session, resets its transport
context, and hands the existing state back to the new connection. If
the token has expired (session was reaped after IdleTimeout) the
server returns ErrTokenExpired and the client falls back to a fresh
session gracefully.
Extended protocol event kinds
live.Event now carries three kinds beyond the original "event":
| Kind | New fields | Use case |
|---|---|---|
"form" |
FormData map[string]string |
Full form submission in one frame |
"navigate" |
Path string |
Client-side navigation within a live page |
"reattach" |
Token string |
Reconnect with existing session |
DecodeEvent validates the Kind field against the known set and
returns an error for unknown kinds, preventing malformed frames from
reaching handlers.
WithLiveOptions on pkg/nguyen.App
Hub tuning is now exposed via the programmatic API:
app := nguyen.New(
nguyen.WithLiveOptions(server.LiveOptions{
AllowedOrigins: []string{"https://example.com"},
AuthFunc: func(c *fiber.Ctx) error {
return myAuth.ValidateSession(c)
},
}),
)Dropped message observability
Session.DroppedMessages (atomic counter) increments whenever the
outbound buffer is full and a message is dropped. Hub.DroppedTotal()
aggregates across all live sessions and is surfaced in
/_nguyen/health:
{
"live": {
"sessions": 42,
"dropped_messages": 0
}
}Bug fixes
ng-for unused index variable — compile error
<ng-for> without an index attribute previously emitted
for i, item := range items where i was never used, causing a Go
compile error in every generated WASM file that used ng-for. Fixed:
the loop now emits for _, item := range items when index is absent.
Named slot regex — multi-line content silently dropped
replaceNamedSlots used (.*?) without the (?s) flag, so any slot
content spanning more than one line was silently discarded. Fixed with
(?s)(.*?).
tokenizeHTML — <script>/<style> content corrupted
The transpiler's HTML tokenizer treated < inside <script> and
<style> blocks as tag opens, corrupting the parse tree. Fixed: after
opening a raw-text tag the tokenizer now scans forward to the matching
close tag and emits the inner content as a single text token.
WASM reconciler — event delegation not wired
mountToDOM in pkg/core/reconciler.go silently skipped onClick,
onChange and other on* props with a bare continue. Fixed: these
props are now written as data-nguyen-{event} attributes so the
existing SetupEventDelegation listener picks them up correctly.
Security / correctness
| Item | Detail |
|---|---|
WrapHTML locale |
Accepts a locale string param; no longer hardcodes lang="en". Callers pass the i18n locale from request context. |
| CSP nonce | injectHydrationMarkers accepts a nonce string; emits <script nonce="..."> when non-empty. Pass c.Locals("csp_nonce") from your middleware. |
pathToHash |
Extended from 48-bit (12 hex chars) to 64-bit (16 hex chars), eliminating birthday collision risk at scale. |
RevalidateTag |
Now calls BackgroundRevalidate instead of the synchronous RevalidatePath, so a tag shared by many pages no longer blocks the caller. |
| ISR Lifecycle | NewISRWithContext(ctx, dir) integrates the cleanup goroutine with pkg/concurrent.Lifecycle. NewISR remains as a backward-compatible wrapper. |
Compatibility
Fully backward compatible with v1.2.x. The only public API additions are:
live.ErrTokenExpired— new sentinel errorlive.Event.FormData,live.Event.Path,live.Event.Token— new fields (zero-value safe)live.Outbound.Token— new fieldlive.Hub.Reattach(ctx, token)— new methodlive.Hub.DroppedTotal()— new methodlive.Session.DroppedMessages— exported atomic fieldlive.Session.ReattachToken— exported fieldrender.WrapHTML(body, result, locale)— addedlocaleparam (pass""for"en")render.injectHydrationMarkers— internal, nonce param (empty = no nonce)cache.NewISRWithContext(ctx, dir)— new constructor
What's next
v1.3.1 will land:
.live.goxcodegen — a single file with frontmatter struct + methods- HTML template becomes a registered
livepage.Pageautomatically.
The parser already setsFile.IsLive = true; the compiler pass is
the remaining work.
- HTML template becomes a registered
- Streaming partial renders (Telegram Protocol foundation).
Install
go install github.com/dev2k6/Nguyen.go/cmd/nguyen@v1.3.0
go get github.com/dev2k6/Nguyen.go@v1.3.0Author
Thái Nguyên thainguyen.junior@gmail.com
v1.2.2
v1.2.2 — Security Patch (Medium/Low)
Released: 2026-05-15
Follow-up security patch addressing the medium and low severity
findings from the v1.2.0 audit. No API changes; fully backward
compatible with v1.2.x.
Security fixes
Medium: Image decompression bomb DoS
File: internal/optimizer/image.go
DecodeImage called image.Decode directly, which decompresses the
full image into memory before any size check. A crafted JPEG/PNG
(e.g. 10000×10000 pixels at 100 KB compressed → 400 MB in RAM) could
exhaust server memory.
Fix: DecodeImage now calls image.DecodeConfig first to read
only the image header. If either dimension exceeds 8192 pixels the
function returns an error without decoding the full image. The limit
is configurable via the maxImageDimension constant.
Medium: S3 upload io.ReadAll OOM
File: internal/upload/s3.go
S3Storage.Put called io.ReadAll(reader) without a size limit.
A client that declared a small Content-Length but sent a large body
could exhaust server memory.
Fix: The reader is now wrapped with io.LimitReader(reader, size+1)
before io.ReadAll. If the actual body exceeds the declared size the
upload is rejected with an error. When size is unknown (≤ 0) a
32 MB default cap is applied.
Medium: CSRF cookie missing explicit SameSite documentation
File: internal/csrf/csrf.go
The CSRF cookie already defaulted to SameSite=Lax but the code
comment did not explain why HTTPOnly: false is intentional (JS must
read the token for the double-submit pattern). Added a clear comment
to prevent future "fix" PRs from accidentally setting HTTPOnly: true.
Medium: OAuth state cookie missing SameSite attribute
File: internal/auth/oauth.go
The OAuth state cookie had no SameSite attribute, leaving it
unprotected against CSRF attacks on the OAuth callback endpoint.
Fix: SameSite: "Lax" is now set on the oauth_state cookie.
Lax is the correct value here: the cookie must be sent when the
OAuth provider redirects back (a top-level GET navigation), but not
on cross-site sub-resource requests.
Low: concurrent.Pool.Submit channel leak on close race
File: pkg/concurrent/concurrent.go
Between the two select statements in Submit, the pool could close
after the reply channel was created but before it was sent to the
jobs queue. The channel would never be drained, leaking a small
allocation. Added a comment documenting the intent and confirming the
buffered channel (cap 1) means no goroutine blocks.
Low: nguyen vet secret detection rules too narrow
File: internal/vet/vet.go
The previous patterns required ≥ 12 characters and missed:
- Short JWT secrets (< 12 chars but still dangerous)
- Common service key prefixes (
sk_live_,AKIA,SG.) - Private key PEM headers
- Empty string assignments to known secret variable names
Fix: Five patterns now cover the above cases. The minimum length
for generic key/password patterns is reduced to 8 characters.
Upgrade
go get github.com/dev2k6/Nguyen.go@v1.2.2No configuration changes required. The image dimension limit (8192)
and S3 default cap (32 MB) are conservative defaults that cover all
legitimate use cases. If you serve very large images, increase
maxImageDimension in internal/optimizer/image.go or pre-process
images before passing them to the optimizer.
Author
Thái Nguyên thainguyen.junior@gmail.com
v1.2.0
v1.2.0 — Live Mode
Released: 2026-05-15
Live Mode renders an entire page on the server, opens a WebSocket back
to the same route, and pushes a small VDOM patch each time state
changes. Interactive UI ships with 0 KB of compiled Go in the
browser — no TinyGo, no WASM bundle, no client-side state library.
The architecture takes advantage of two Go primitives JavaScript runtimes
cannot match: cheap goroutines (one per WebSocket connection) and
context.Context cancellation that automatically cleans up every
in-flight DB query, timer and helper when a session ends. Phoenix
LiveView demonstrated this pattern for Elixir; v1.2.0 brings it
natively to Go without copying its API or jargon.
Highlights
pkg/live and pkg/livepage — public Live Mode contract
// livepage.Page is the contract for a live route.
type Page interface {
Init(ctx context.Context, params map[string]string) (state any, err error)
Render(ctx context.Context, state any) (string, error)
Handle(ctx context.Context, state any, evt live.Event) (any, error)
}State stays on the server, Render emits HTML, Handle reacts to one
client event. Register pages from your WithSetup hook:
app := nguyen.New(nguyen.WithPort(3000))
app.RegisterLivePage("/counter", &CounterPage{})
app.Listen("")internal/live — Hub, Session and VDOM differ
live.Hubowns sessions process-wide withMaxSessions(default
10000),IdleTimeout(60s),HeartbeatInterval(30s) and
OutboundBuffer(16) options.live.Sessionruns one goroutine per connection, drains an outbound
channel, and recovers from panic in handlers (turning them into
structured error frames so the session stays open).live.Diff(prev, next)produces minimal patch ops:text,attr,
replace,insert,remove, plus arootfallback when the tree
shape changes too much.
/_nguyen/live.js — tiny browser runtime
A ~6 KB script auto-mounted by App.serve() discovers
<div data-nguyen-live="/route"> elements, opens a WebSocket, applies
patches and forwards events. Built-in features:
- Event delegation for
click,submit,input,change,keydown,
keyup,focus,blur. - Auto-reconnect with exponential backoff (capped at 30s).
- Server-driven heartbeat (30s ping) keeps NAT/proxy intermediaries
from idle-closing. - Form submits and intra-page anchors have their default action
prevented so the session stays alive.
.gox parser detection
A page opts into Live Mode by either:
- file suffix
.live.gox, or - frontmatter directive
//+nguyen:live.
The parser sets File.IsLive = true. A future codegen pass (v1.3) will
turn the frontmatter struct + methods into a livepage.Page
automatically.
Wire protocol
Client → server (one inbound event):
{"t":"event","name":"increment","target":"0/1","value":null}Server → client:
{"t":"replace","html":"<...>"}
{"t":"diff","ops":[{"op":"text","path":"0/1","value":"Count: 4"}]}
{"t":"ping"}
{"t":"error","error":"..."}JSON is used in v1.2.0 for debuggability; binary framing is on the
roadmap once we have real deployments to benchmark against.
Example
examples/hello-nguyen ships a working Live counter:
pages/live-counter.gox— host page with the<div data-nguyen-live>
mount and the<script src="/_nguyen/live.js">tag.livedemo/counter.go— implementslivepage.Pagefor/counter.cmd/server/main.go— wires the page in viaapp.RegisterLivePage.
Run it with:
cd examples/hello-nguyen
go run ./cmd/serverThen open http://localhost:3000/live-counter.
Documentation
- docs/live-mode.md — full guide: architecture,
page contract, event delegation, patch ops, hub tuning, failure modes - docs/agent-reference.md — Live Mode
section so AI assistants emit the correct boilerplate - README.md — updated feature list
Compatibility
This release is fully backward compatible with v1.1.x. No .gox
syntax changed. Live Mode is opt-in: applications that do not call
RegisterLivePage see no extra goroutines, no extra HTTP handlers
beyond /_nguyen/live.js (served lazily) and no behavioural change.
Performance and limits
- One Live session ≈ 10–20 KB of Go state. 10K concurrent sessions
≈ 100–200 MB of RAM, well within a single-VM budget. - Round-trip per interaction is bounded by network latency (typically
30–100 ms over the public internet, sub-millisecond over a LAN). - The differ is structural; child-count changes fall back to a
replaceof the parent. Keyed reconciliation (<li :key="...">)
is on the v1.3 roadmap.
When to use Live Mode
- Admin panels, dashboards, internal tooling.
- Form flows, multi-step wizards, anything mostly server-driven.
- Pages where you want zero client-side bundle for ergonomic and
security reasons.
Skip it for sub-frame UI (drag, canvas, animation) or offline-capable
pages — keep the existing CSR / SSR + WASM model for those.
What's next
v1.3 will land:
.live.goxcodegen so a single file with frontmatter + template
becomes a registered live page.- Keyed reconciliation in the differ.
- Persistent session reattach (state survives reconnect).
- Streaming partial renders (Telegram Protocol foundation).
Install
go install github.com/dev2k6/Nguyen.go/cmd/nguyen@v1.2.0
go get github.com/dev2k6/Nguyen.go@v1.2.0Author
Thái Nguyên thainguyen.junior@gmail.com
v1.1.0
v1.1.0 — Foundation Primitives
Released: 2026-05-15
This release lays the foundation for the next generation of Nguyen.go.
Five Go primitives — goroutines, context.Context, go/ast, embed.FS,
and stdlib observability — are now first-class citizens of the
framework, exposed through new packages, middleware and CLI tooling.
Highlights
Concurrency primitives — pkg/concurrent
Typed wrappers around the CSP patterns Nguyen.go applications use most.
Pool[J, R]— bounded worker pool with typed jobs/results,
panic recovery and ctx-aware cancellation.Map— fan-out across N workers preserving input order, first
error cancels the rest.Parallel— fire-and-collect for independent loaders.Race— first-success-wins across multiple sources, with
ErrAllSourcesFailedreporting every failure.Deadline— bound any function with a timeout.Lifecycle— coordinate ordered start/stop of long-lived
components (HTTP, WS, DB, cron, brokers) with stop-grace.
Request-scoped context — pkg/ngctx
Standard typed keys for request-scoped values:
WithTraceID/TraceIDWithRequestID/RequestIDWithLocale/LocaleWithUser/User(carriesID,Email,Roles,Extra)WithBudget/BudgetFromHTTP(ctx, headers)populates trace/request/locale from a request
These keys are populated automatically by the new
server.ContextMiddleware so loaders and actions read them via
context.Context rather than Fiber's Locals map.
Structured logging + spans — pkg/observe
Built on Go 1.21+'s log/slog.
observe.Default()/SetDefault()— swap the root logger
(text or JSON) at startup.observe.Logger(ctx)— returns a logger automatically enriched with
trace_id,request_id,locale,user_idfromngctx.observe.NewJSON(w, level)— production-ready JSON handler for
Datadog / Loki / GCP Logging.observe.StartSpan(ctx, name)/Span.End(err)— lightweight
timing helper that emits a structured event when ended.
The framework's HTTP server now emits one http event per request
with method, path, status, duration and trace IDs, and writes
X-Trace-Id / X-Request-Id response headers.
nguyen doctor
Pre-flight diagnostics for the host environment and project layout.
Reports Go version, TinyGo availability, Tailwind binary, presence of
pages/, public/, styles/, config/nguyen.config.yml, and git
status. Exits non-zero only on hard prerequisites — safe for CI.
nguyen vet
Static analysis for .gox files. Six rules ship in the box:
| Rule | Severity |
|---|---|
context-first-param |
warning |
no-time-sleep |
warning |
goroutine-cancellation |
warning |
no-hardcoded-secrets |
error |
no-fmt-print |
warning |
event-handler-exists |
error |
--strict promotes warnings to errors for CI gating. The rule engine is
the seed of the future Effect Wall.
nguyen generate routes
Reads the pages directory, sorts routes deterministically and emits a
typed Go file with one accessor per route. Static routes return
constants; dynamic routes accept typed parameters.
<a href={routes.BlogBySlug(post.Slug)}>{post.Title}</a>A typo becomes a compile error rather than a 404 at runtime. The
generated file is byte-identical across machines, so it diffs cleanly
in code review and works inside reproducible builds.
nguyen dist
Cross-compile reproducible single-binary releases.
nguyen dist --targets linux/amd64,linux/arm64,darwin/arm64When --reproducible (default on), dist sets -trimpath,
-buildvcs=false, strips symbols and the build ID, and disables CGo.
Two builds from the same source on different machines produce
byte-identical binaries — supply-chain verifiable, air-gap deployable.
Documentation
Three new guides:
- docs/concurrency.md — Pool, Map, Parallel, Race, Deadline, Lifecycle
- docs/observability.md — context keys, logging, spans
- docs/tooling.md — doctor, vet, generate, dist
docs/cli.md and the README are extended with the new
commands.
Compatibility
This release is fully backward compatible with v1.0.x. No .gox
syntax changed. All new packages live under pkg/concurrent,
pkg/ngctx, pkg/observe and do not affect existing imports.
The new ContextMiddleware and AccessLog are wired into the default
Fiber stack. If you were calling .Fiber().Use(...) to register your
own logging middleware, both stacks coexist without conflict, but you
may want to disable yours and read observe.Logger(ctx) instead.
What's next
v1.1.0 is the first half of the Foundation Primitives series. v1.2.0
will land the second half:
- Spore — component-as-process model with bounded mailboxes and
supervision trees - Effect Wall — full effect-class inference using
go/typesso
build failures replace runtime "DB call from client component"
surprises - Telegram Protocol — typed streaming wire format for SSR with
out-of-order rendering and per-frame error recovery
Install
go install github.com/dev2k6/Nguyen.go/cmd/nguyen@v1.1.0Or upgrade an existing project:
go get -u github.com/dev2k6/Nguyen.go@v1.1.0Author
Thái Nguyên thainguyen.junior@gmail.com
v1.0.7
What's New
Nguyen.go v1.0.7 adds a full range of backend modules, transforming the framework into a full-stack solution for all types of web applications.
New Modules
- Database — Connection pool, query builder (SELECT/INSERT/UPDATE/DELETE), transaction support
- Migration — SQL migration system với CLI (
nguyen migrate up/down/create/status) - Auth — JWT (HS256), refresh token, bcrypt password hashing, session store, OAuth2 (Google, GitHub, Facebook)
- Auth Middleware —
Required(),Role("admin"),Optional() - CSRF — Token generation, constant-time validation, auto-rotate, configurable skip paths
- Validator — 13+ built-in rules, struct tag validation, map validation, Fiber middleware integration
- Upload — Local filesystem + S3 storage, single/multiple file handlers, MIME type filtering
- WebSocket — Hub pattern, rooms, broadcast, send-to-client, ping/pong keepalive
- i18n — JSON translation files, nested key support, locale detection (URL/cookie/header/query)
- Mail — SMTP with TLS, HTML templates, attachments, CC/BCC
- Event Bus — Sync/async event emission, multiple handlers per event
- Rate Limit — Per-endpoint rate limiting with custom key functions
Infrastructure
- Docker — Multi-stage Dockerfile, docker-compose (app + PostgreSQL + Redis)
- Graceful shutdown — Database connection cleanup on SIGTERM
Framework Integration
- 7 new config sections in
nguyen.config.yml - 7 new functional options:
WithDatabase,WithAuth,WithCSRF,WithUpload,WithWebSocket,WithI18n,WithMail - Accessor methods:
app.DB(),app.Auth(),app.CSRF(),app.Uploader(),app.WSHub(),app.I18n(),app.Mailer(),app.Events() - Response helpers:
ValidationError,Conflict,TooManyRequests
Dependencies Added
golang.org/x/crypto— bcrypt password hashinggithub.com/gofiber/websocket/v2— WebSocket support
Documentation
docs/backend-modules.md— Full usage guide for all new modules
Full Changelog: v1.0.6...v1.0.7
v1.0.6
V1.0.6
Highlights
This release consolidates all improvements since v1.0.1 into a single, clean version with full documentation and synchronized pkg.go.dev publishing.
Breaking Changes
- File extension renamed:
.nguyen→.gox
All component files now use the.goxextension — shorter, cleaner, easier to type. The.nguyen/output directory and framework name remain unchanged.
New Features
SPA Navigation Engine (rewritten)
- DOM morphing instead of
innerHTML— preserves focus, scroll position, form state - Binary frame protocol for minimal network payload
- Batch prefetch endpoint (
POST /_nguyen/prefetch) — Go renders multiple routes concurrently via goroutines AbortControllercancels stale in-flight navigationsrequestIdleCallbackscheduling for zero main-thread blocking- View Transitions API support
- In-memory LRU partial cache (128 entries, 60s TTL)
Data Loaders
- Concurrent server-side data fetching via
LoaderRegistry - Multiple loaders execute in parallel using goroutines
- Context timeout and panic recovery built-in
Error Boundaries
- Graceful render failure handling with customizable fallback HTML
- Prevents a single broken page from crashing the server
Template Directives
<nguyen-fragment>— render children without wrapper div<nguyen-show when="key">/<nguyen-hide when="key">— conditional rendering evaluated against SSR state
Middleware System
- Named middleware registry with route guard resolution
- Built-in
CORSandRequestIDmiddleware - Guards declared in
.goxfrontmatter:guard = "auth"
Response Helpers
OK,Created,NoContent,BadRequest,Unauthorized,Forbidden,NotFound,ServerErrorRedirect,RedirectPermanentPaginatedwith full metadata (page, total, has_next, has_prev)
Environment Injection
- Safe access to
NGUYEN_prefixed env vars in SSR context IsProduction()/IsDevelopment()helpers
Health Check
- Built-in
/_nguyen/healthendpoint - Reports: status, version, uptime, Go version, goroutine count, memory stats
Improvements
- Centralized version string (
internal/version/version.go) - Dev server rate limiter increased to 600 req/min (HMR-friendly)
- Pre-compiled regex in scoped CSS processing
strconv.Itoareplaces hand-rolled implementation- Tailwind CLI uses latest release URL
Documentation
- Full README.md rewrite with architecture diagram
- All 15 docs pages updated to
.goxextension - Example templates updated
Retracted Versions
The following versions are retracted in go.mod due to incomplete publishing:
v1.0.2,v1.0.3,v1.0.4
Use v1.0.6 (or v1.0.1 / v1.0.5) instead.
Migration from v1.0.1
# Rename all component files
# Linux/macOS:
find pages/ app/ components/ -name "*.nguyen" -exec sh -c 'mv "$1" "${1%.nguyen}.gox"' _ {} \;
# Windows PowerShell:
Get-ChildItem -Recurse -Filter "*.nguyen" | Rename-Item -NewName { $_.Name -replace '\.nguyen$', '.gox' }
# Update Tailwind config content paths
# "*.nguyen" → "*.gox"v1.0.1
Nguyen.go V1.0.1
Improvements
- Centralized version management — version string now lives in
internal/version/version.go, eliminating duplicate hardcoded values acrossroot.goandconfig.go - Dev server rate limiter — increased from 100 to 600 req/min/IP in dev mode to prevent false blocks during HMR rapid reloads
- Performance: scoped CSS — pre-compiled regex patterns instead of compiling inside loops on every call
- Stdlib usage — replaced hand-rolled
Itoaimplementation withstrconv.Itoa - Tailwind CLI download — uses latest release URL instead of hardcoded version, ensuring new projects always get the most recent Tailwind build
Fixes
- Use
@latestinstead of hardcoded version in install commands - Correct clone URL (removed duplicate
.go)
Full Changelog
36ebc0b...761328f
Nguyen.go v1.0.0
Nguyen.go v1.0.0 — Initial Release
The Go full-stack framework — file-based routing, islands architecture, and React-style hooks compiled to WebAssembly. One binary to deploy.
Highlights
- File-system routing — drop a
.nguyenfile inpages/, get a route - React-style hooks —
UseState,UseEffect,UseMemo,UseCallback,UseRef,UseContext - Hybrid rendering — SSR, ISR, CSR per page + streaming SSR
- Islands architecture — partial hydration with
client:load,client:idle,client:visible - Per-route WASM code splitting — sub-100KB chunks via TinyGo
- Single binary deploy —
go build→ one executable, no Node.js, no Docker required - Built-in image optimization — WebP/AVIF conversion at
/_nguyen/image - PWA support — auto-generated manifest.json + service worker
- GEO (SEO for AI) — sitemap.xml, robots.txt, llms.txt, JSON-LD structured data
- HMR — SSE-based hot module replacement in dev mode
- Scoped CSS — component-level styles via
<style scoped> - Tailwind CSS — standalone CLI integration (no npm)
Install
go install github.com/dev2k6/Nguyen.go/cmd/nguyen@latest