Skip to content

Latest commit

 

History

History
620 lines (521 loc) · 20.9 KB

File metadata and controls

620 lines (521 loc) · 20.9 KB

Config Reference

The root package exposes config types used by compiler and future generated app behavior.

type Config struct {
	AppName string
	Source  SourceConfig
	Modules []ModuleConfig
	Render  RenderConfig
	Env     EnvConfig
	Build   BuildConfig
	CSS     CSSConfig
	Addons  []Addon
}

Source

gowdk.config.go is required for CLI commands that compile, validate, inspect, or serve a development loop for .gwdk code: check, manifest, sitemap, routes, inspect ir, build, and dev. Those commands load gowdk.config.go from the current directory by default, or the file passed with --config <file>.

SourceConfig has include and exclude patterns. Discovery support exists in internal/discover, and gowdk build reads literal Source.Include and Source.Exclude fields from the loaded config when no explicit files are supplied. Explicit file paths still require a loaded config.

Modules declares named source groups. Build discovery treats modules as source selectors. Generated app and binary composition is controlled by the modules selected for a specific build command.

Supported initial config subset:

package app

import "github.com/cssbruno/gowdk"

var Config = gowdk.Config{
	Source: gowdk.SourceConfig{
		Include: []string{"src/**/*.gwdk"},
		Exclude: []string{"src/**/draft.page.gwdk"},
	},
	Modules: []gowdk.ModuleConfig{
		{Name: "frontend", Type: "frontend"},
		{
			Name: "frontend2",
			Type: "marketing-ui",
			Source: gowdk.SourceConfig{
				Include: []string{"ui2/**/*.gwdk"},
			},
		},
		{
			Name: "backendmicroservice",
			Type: "backendmicroservice",
			Source: gowdk.SourceConfig{
				Include: []string{"services/backend/**/*.gwdk"},
				Exclude: []string{"services/backend/**/draft.page.gwdk"},
			},
		},
	},
	Build: gowdk.BuildConfig{
		Output: "dist/site",
		Mode: gowdk.Production,
		Head: gowdk.HeadConfig{
			SiteName: "Example",
			Favicon: "/favicon.ico",
			Image: "https://example.com/social.png",
			TwitterCard: "summary",
		},
		Stylesheets: []gowdk.Stylesheet{
			{Href: "/assets/app.css"},
		},
		Scripts: []gowdk.Script{
			{Src: "/assets/app.js", Type: "module"},
		},
		Targets: []gowdk.BuildTargetConfig{
			{
				Name: "admin",
				Modules: []string{"admin"},
				Output: "dist/admin",
				App: ".gowdk/admin",
				Binary: "bin/admin",
				WASM: "bin/admin.wasm",
				DeployRecipes: []string{"systemd"},
			},
			{
				Name: "public-admin",
				Modules: []string{"public", "admin"},
				Output: "dist/public-admin",
				App: ".gowdk/public-admin",
				Binary: "bin/public-admin",
			},
		},
	},
	Env: gowdk.EnvConfig{
		Vars: []gowdk.EnvVar{
			{Name: "GOWDK_BACKEND_ORIGIN", Required: true},
			{Name: "GOWDK_ADDR", Default: "127.0.0.1:8080"},
		},
		Secrets: []gowdk.SecretEnv{
			{Name: "DATABASE_URL", Required: true},
			{Name: "GOWDK_CSRF_SECRET", Required: true},
		},
	},
	CSS: gowdk.CSSConfig{
		Include: []string{"styles/**/*.css"},
		Default: []string{"global", "tokens"},
	},
}

The CLI first parses this file as a literal config subset. Unknown top-level gowdk.Config fields are rejected instead of silently ignored. When a supported field contains non-literal Go that the subset cannot reduce safely, the loader falls back to the executable config bridge described below.

Addon constructors outside the built-in AST subset are loaded through a small Go helper that imports the config package. That means addon packages are normal Go modules:

import brand "github.com/example/gowdk-brand"

var Config = gowdk.Config{
	Addons: []gowdk.Addon{
		brand.Addon(),
	},
}

The addon module may import other GitHub/private/local modules. The project go.mod remains the source of truth for resolving those imports, including require, replace, GOPRIVATE, and module proxy configuration.

Generated App Request Guards

When generated SSR, action, API, or fragment routes declare guard, the generated app package can expose guard registration hooks. If auth.Addon is configured, generated startup registers the default auth.required guard and a session-backed provider for native role: / permission: guard IDs from the addon options.

Custom guard IDs still require a generated app hook:

package gowdkapp

import gowdkguard "github.com/cssbruno/gowdk/runtime/guard"

func GOWDKGuardRegistry() gowdkguard.Registry {
	return gowdkguard.Registry{
		"auth.required": func(ctx gowdkguard.Context) error {
			return nil
		},
	}
}

Native RBAC guard IDs such as role:admin and permission:patients.read use an application-owned principal source instead of a custom guard function:

import (
	"net/http"

	gowdkauth "github.com/cssbruno/gowdk/runtime/auth"
)

func GOWDKAuthProvider() gowdkauth.Provider {
	return gowdkauth.ProviderFunc(func(request *http.Request) (*gowdkauth.Principal, error) {
		return &gowdkauth.Principal{ID: "user-1", Roles: []string{"admin"}}, nil
	})
}

This file belongs with generated app startup code, not inside feature packages that declare handlers. Missing required backing functions fail the generated app Go build when no addon supplies them. Guard errors still return HTTP 403 before SSR load functions, action decoding, API handlers, or user business logic run.

Native RBAC guards are a defense-in-depth redundancy layer for generated route/page access. They must never replace backend authorization inside handlers, services, repositories, or external systems.

See hooks.md for guard, rate-limit, and middleware ordering.

Modules

ModuleConfig names a logical source group:

type ModuleConfig struct {
	Name   string
	Type   string
	Source SourceConfig
}

Type is user-defined metadata in the current build slice. GOWDK does not validate or reserve type values, so projects can use values such as frontend, frontend2, backend, backendmicroservice, worker, or any local module role. Deployment code remains user-owned; GOWDK does not infer Kubernetes or deployment settings from module type.

When gowdk build discovers files and a module has a name but no Source.Include, it uses <module-name>/**/*.gwdk. For example, {Name: "frontend"} discovers frontend/**/*.gwdk. Explicit module include patterns override that default for the module. Root Source.Exclude and module Source.Exclude patterns are both honored. gowdk build --module <name> limits discovery to selected configured modules.

The selected modules define what gets compiled into the build output and, when --app or --bin is used, what is copied into the generated app and embedded in the generated binary. Ad hoc CLI flags can still package modules directly:

# Single-module binary.
gowdk build --module admin --out dist/admin --app .gowdk/admin --bin bin/admin

# Multi-module binary.
gowdk build --module public --module admin --out dist/app --app .gowdk/app --bin bin/app

# Separate binaries with different module sets.
gowdk build --module public --out dist/public --app .gowdk/public --bin bin/public
gowdk build --module admin,api --out dist/admin-api --app .gowdk/admin-api --bin bin/admin-api

Use distinct --out and --app directories for separate binaries so stale artifacts from another module selection cannot be copied into the next binary.

Render

RenderConfig.Default controls the default render mode. When omitted, default mode is spa.

Env

EnvConfig declares the runtime environment contract. Config owns the names, required flags, and safe non-secret defaults. Deployment owns all values.

type EnvConfig struct {
	Vars    []gowdk.EnvVar
	Secrets []gowdk.SecretEnv
}

type EnvVar struct {
	Name     string
	Required bool
	Default  string
}

type SecretEnv struct {
	Name     string
	Required bool
}

Use Vars for normal runtime settings:

Env: gowdk.EnvConfig{
	Vars: []gowdk.EnvVar{
		{Name: "GOWDK_ADDR", Default: "127.0.0.1:8080"},
		{Name: "GOWDK_BACKEND_ORIGIN", Required: true},
	},
}

Use Secrets for secret names:

Env: gowdk.EnvConfig{
	Secrets: []gowdk.SecretEnv{
		{Name: "DATABASE_URL", Required: true},
		{Name: "GOWDK_CSRF_SECRET", Required: true},
	},
}

Validation runs when gowdk.config.go is loaded. Required names that are unset or blank in the host environment fail with a direct diagnostic such as DATABASE_URL is required but is not set. Required vars with Default are treated as satisfied by the default. Secrets have no Default or value field by type; Default and Value are rejected in literal config parsing too.

Project-aware CLI commands can load local env files before validation:

gowdk check --env-file .env.dev
gowdk build --env-file .env.production

If --env-file is omitted, GOWDK auto-loads .env.<GOWDK_ENV> from the project root when GOWDK_ENV is set and the file exists, otherwise .env when present. Process environment values always win over file values. The file is only a value source for the same validation contract; it does not bypass Required or MinBytes.

The same name cannot appear in both Vars and Secrets. Secret-looking var names ending in _SECRET, _TOKEN, _PASSWORD, or _KEY are rejected and must move to Secrets. Diagnostics print names only and never print values.

Generated app binaries repeat the required env check before serving requests. For direct binary runs, set GOWDK_ENV_FILE=/path/to/.env or place .env in the process working directory. This is a startup redundancy layer only. It does not replace backend authorization, handler validation, database checks, deployment secrets, or runtime-specific security controls.

Build

BuildConfig.Output, BuildConfig.Mode, BuildConfig.Assets, BuildConfig.ObfuscateAssets, BuildConfig.Head, BuildConfig.CSRF, BuildConfig.SecurityHeaders, BuildConfig.BodyLimits, BuildConfig.AllowMissingBackend, BuildConfig.Stylesheets, BuildConfig.Scripts, and BuildConfig.Targets are target build settings. Current gowdk build reads literal Build.Output, Build.Mode, Build.ObfuscateAssets, Build.Head, Build.CSRF, Build.SecurityHeaders, Build.BodyLimits, Build.AllowMissingBackend, Build.Stylesheets, Build.Scripts, and Build.Targets from gowdk.config.go; --out overrides Build.Output for ad hoc builds and --obfuscate-assets overrides Build.Mode to production for that build. BuildConfig.Assets remains planned.

Build.Targets declares repeatable module-to-output packaging:

type BuildConfig struct {
	Output              string
	Mode                gowdk.BuildMode
	Assets              gowdk.AssetMode
	ObfuscateAssets     bool
	Head                gowdk.HeadConfig
	CSRF                gowdk.CSRFConfig
	SecurityHeaders     gowdk.SecurityHeadersConfig
	BodyLimits          gowdk.BodyLimitsConfig
	AllowMissingBackend bool
	Stylesheets         []gowdk.Stylesheet
	Scripts             []gowdk.Script
	Targets             []gowdk.BuildTargetConfig
}

type HeadConfig struct {
	SiteName    string
	Favicon     string
	Image       string
	TwitterCard string
}

type CSRFConfig struct {
	Enabled    bool
	Disabled   bool
	SecretEnv  string
	CookieName string
	FieldName  string
	HeaderName string
	Insecure   bool
}

type SecurityHeadersConfig struct {
	Enabled bool
	Headers map[string]string
}

type BodyLimitsConfig struct {
	ActionBytes int64
	APIBytes    int64
}

type Script struct {
	Src  string
	Type string
}

type BuildTargetConfig struct {
	Name          string
	Modules       []string
	Output        string
	App           string
	Binary        string
	WASM          string
	BackendApp    string
	BackendBinary string
	DeployRecipes []string
}

Mode controls development metadata in generated frontend artifacts. The default omitted mode behaves like gowdk.Development and emits JavaScript island source maps. Set Mode: gowdk.Production to omit .js.map artifacts and sourceMappingURL comments and to compact generated island JavaScript.

ObfuscateAssets is a production-only optimization/hardening switch for compiler-owned generated browser JavaScript such as the SPA/partial runtime, store runtime, island runtime/stubs, and WASM loader glue. It uses deterministic minification/identifier shortening, disables generated source maps through production mode, records transformed assets in gowdk-assets.json, and writes asset_obfuscation / asset_obfuscated build-report events. It is not a security boundary and does not replace server-side auth, guards, CSRF, validation, or handler authorization. Configs that set ObfuscateAssets: true must also set Mode: gowdk.Production; the CLI flag --obfuscate-assets sets both for the current build.

Production mode also requires explicitly declared act and api endpoints to bind to supported same-package Go handlers. Missing or unsupported handlers fail the build by default. Set AllowMissingBackend: true or pass --allow-missing-backend when intentionally generating HTTP 501 stubs during a migration.

Head controls app-level document head tags. Favicon emits <link rel="icon">. SiteName, Image, and TwitterCard enable generated Open Graph and Twitter metadata. A page-level image overrides Head.Image for that page.

Scripts declares global script tags emitted into every GOWDK-generated HTML document. Use page or component js "./file.js", js "./file.ts", or inline js {} declarations when a browser module should be emitted and linked only where that page/component is used.

CSRF controls generated action and web-command CSRF wiring. CSRF is enabled by default for generated state-changing form endpoints. Generated apps require a signing secret from SecretEnv or GOWDK_CSRF_SECRET, inject a hidden token field into served HTML POST forms, and validate POSTs before generated decoding or user handlers run. Invalid or missing tokens return HTTP 403 with invalid csrf token and Cache-Control: no-store. Set Disabled: true only for an intentional non-production/test opt-out. Enabled is retained for older configs but is no longer required. CookieName, FieldName, and HeaderName override the generated token transport names. Insecure is for local HTTP development only: it disables the Secure cookie flag, uses the default cookie name gowdk-csrf instead of __Host-gowdk-csrf, and rejects explicit __Host-/__Secure- cookie names because browsers require those prefixes to be Secure.

SecurityHeaders controls additional headers written by generated app handlers. When Enabled is true, each entry in Headers is passed to runtime/app and emitted on every generated response path, including health checks and generated errors. Use it for app-owned headers such as X-Content-Type-Options, Referrer-Policy, Content-Security-Policy, and X-Frame-Options. Keep TLS-boundary headers such as Strict-Transport-Security at the HTTPS edge unless the generated app is directly responsible for TLS.

BodyLimits controls generated request body caps in bytes. Omitted or non-positive values use the default 1 MiB cap. ActionBytes applies to generated action POST handlers and web command form adapters before form decoding. APIBytes applies to generated API handlers before user code reads the request body.

Name is required. Output is optional and defaults to .gowdk/output/<target-name> when omitted. Modules selects configured modules; omit it to use the default configured discovery set. App is optional and writes a generated Go app that embeds the target output. Binary is optional, requires App, and compiles that generated app for the local platform. WASM is optional, requires App, and compiles the generated app with GOOS=js GOARCH=wasm.

BackendApp is optional and writes a generated backend-only Go app for feature-bound action/API endpoints. BackendBinary is optional, requires BackendApp, and compiles that backend app. When a target has both frontend App/Binary and BackendApp/BackendBinary, the frontend binary proxies generated backend routes to GOWDK_BACKEND_ORIGIN.

DeployRecipes is optional and accepts static, systemd, caddy, nginx, and split. The values map to gowdk build --deploy-recipe and emit starter deployment files for the target shape. They do not add secrets, domains, TLS policy, platform-specific rollout logic, storage, backups, or CDN settings.

When Build.Targets is present, gowdk build runs every configured target unless ad hoc build flags or explicit files are passed. Use gowdk build --target <name> to run one or more named targets; --target may be repeated or comma-separated.

CSS

CSSConfig controls discovered CSS inputs and generated page CSS output:

type CSSConfig struct {
	Include []string
	Exclude []string
	Default []string
	Output  CSSOutputConfig
}

type CSSOutputConfig struct {
	Dir        string
	HrefPrefix string
}

When omitted, CSS discovery scans **/*.css, excludes .git, vendor, node_modules, .gowdk, dist, and the selected build output directory, and uses global.css as the default CSS input when present.

CSS.Default names discovered CSS inputs used by the default built-in in css. Generated page CSS defaults to assets/gowdk/<page-id>.css and hrefs under /assets/gowdk/.

Addons

Addons registers optional features such as spa, actions, partial, SSR, API, embed, CSS, contracts, realtime, auth, DB helpers, rate limiting, and SEO output. Current validation uses feature registration for render-mode, realtime, and other compiler checks; SPA builds invoke addons that implement gowdk.CSSProcessor or gowdk.SEOProvider.

DB helper usage is ordinary Go code around database/sql; see db.md for migrations, transactions, readiness, and sqlc usage.

Use gowdk add --list to print addable built-in addon names, and gowdk add --list --registry to inspect the local discovery metadata. Use gowdk add <name> to insert the canonical import and <name>.Addon() constructor into gowdk.config.go. gowdk add seo requires --base-url <url> because SEO build output requires seo.Options.BaseURL. The command rewrites literal Config.Addons lists only; if Addons is computed by Go code, edit the config manually.

The literal config loader recognizes built-in addon constructors when they are imported from their canonical package paths. Most are no-argument constructors; addons/auth accepts the generated-app-safe session options subset (SecretEnv, CookieName, TTL, Insecure), and addons/seo accepts the literal SEO options subset:

import (
	"github.com/cssbruno/gowdk/addons/actions"
	"github.com/cssbruno/gowdk/addons/api"
	"github.com/cssbruno/gowdk/addons/auth"
	"github.com/cssbruno/gowdk/addons/contracts"
	"github.com/cssbruno/gowdk/addons/css"
	"github.com/cssbruno/gowdk/addons/db"
	"github.com/cssbruno/gowdk/addons/embed"
	"github.com/cssbruno/gowdk/addons/partial"
	"github.com/cssbruno/gowdk/addons/ratelimit"
	"github.com/cssbruno/gowdk/addons/realtime"
	"github.com/cssbruno/gowdk/addons/seo"
	"github.com/cssbruno/gowdk/addons/spa"
	"github.com/cssbruno/gowdk/addons/ssr"
	"github.com/cssbruno/gowdk/addons/static"
)

var Config = gowdk.Config{
	Addons: []gowdk.Addon{
		static.Addon(),
		spa.Addon(),
		actions.Addon(),
		partial.Addon(),
		ssr.Addon(),
		api.Addon(),
		auth.Addon(auth.Options{
			SecretEnv:  "GOWDK_AUTH_SESSION_SECRET",
			CookieName: "gowdk_session",
		}),
		contracts.Addon(),
		embed.Addon(),
		css.Addon(),
		db.Addon(),
		ratelimit.Addon(),
		realtime.Addon(),
		seo.Addon(seo.Options{
			BaseURL: "https://example.com",
		}),
	},
}

If Addons contains a constructor outside that AST-only subset, the loader uses an executable config bridge: it creates a temporary helper inside the project module, imports the config package as normal Go, and reads the resulting gowdk.Config. That allows addons from other modules, including GitHub-hosted addons, to participate through the regular gowdk.Addon, gowdk.CSSProcessor, gowdk.SEOProvider, and gowdk.GoBlockConsumer interfaces:

import (
	"github.com/cssbruno/gowdk"
	"github.com/example/gowdk-brand"
)

var Config = gowdk.Config{
	Addons: []gowdk.Addon{
		brand.Addon(),
	},
}

External addon dependencies must already be resolvable by the project module, for example with go get github.com/example/gowdk-brand. Config packages must be importable Go packages, not package main.

The literal loader also recognizes the known literal Tailwind addon options subset without needing the executable bridge:

import "github.com/cssbruno/gowdk/addons/tailwind"

var Config = gowdk.Config{
	Addons: []gowdk.Addon{
		tailwind.Addon(tailwind.Options{
			Input:  "styles/app.css",
			Minify: true,
		}),
	},
}

When Command is omitted, the Tailwind addon uses tailwindcss from PATH. If the executable is missing, builds fail with an install-required error. The executable bridge runs project config code only when the AST-only loader finds addon constructors it cannot reduce safely.

When ratelimit.Addon() is enabled, generated apps with request-time action, API, fragment, SSR, or split-backend proxy routes expose gowdkapp.RegisterRateLimiter(*ratelimit.Limiter). User-owned Go still creates the limiter and chooses the in-memory store, Redis store adapter, key function, limit, and window.