goflare

package module
v0.2.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: MIT Imports: 21 Imported by: 1

README

GoFlare

GoFlare is a self-contained Go tool (library + CLI) for deploying Go WASM projects to Cloudflare Workers and Pages. No Node.js, no Wrangler, no GitHub Actions. Pure Go, direct Cloudflare API.

When to use

  • Cloudflare Pages Functions in Go (recommended) — static site + Go edge function deployed via CF Git Integration
  • Standalone Cloudflare Workers in Go (WASM)
  • Static Cloudflare Pages (Go WASM frontends)

The project mode is inferred from edge/main.go imports — no MODE variable in .env. See BUILD_PAGES_FUNCTIONS.md.

my-project/
├── .env                       # credentials — gitignored (NEVER tokens)
├── .env.example
├── routes/
│   └── routes.go              # build-agnostic — func Register(r router.Router)
├── modules/
│   └── contact/
│       ├── model.go           # build-agnostic — model + Validate()
│       └── handler.go         # build-agnostic — func Handle(ctx router.Context)
├── web/
│   ├── client.go              # //go:build wasm — frontend (browser)
│   ├── server.go              # //go:build !wasm — local dev server
│   └── public/                # static assets — committed; produced by tinywasm framework
│       ├── index.html
│       ├── client.wasm
│       ├── script.js
│       └── style.css
├── edge/
│   └── main.go                # //go:build wasm — entrypoint, imports goflare/pages
└── functions/                 # generated by goflare — COMMITTED
    ├── [[path]].mjs           # catch-all glue (exports onRequest)
    └── edge.wasm              # compiled edge/main.go

Project layout — Workers (legacy)

my-project/
├── .env
├── edge/
│   └── main.go                # imports goflare/workers (workers.Handle)
└── .build/                    # gitignored worker artifacts
    ├── edge.js
    └── edge.wasm

.env

PROJECT_NAME=my-app
CLOUDFLARE_ACCOUNT_ID=your-account-id
PUBLIC_DIR=web/public            # optional, default: web/public
FUNCTIONS_DIR=functions          # optional, default: functions
# ENTRY=edge                     # optional, auto-detected if edge/main.go exists
# DOMAIN=example.com             # optional, custom domain for Pages

NEVER put CLOUDFLARE_API_TOKEN in .env. The token lives in the OS keyring via goflare auth.

CLI

Install the CLI:

go install github.com/tinywasm/goflare/cmd/goflare@latest
  • goflare init [--mode=pages-functions|workers|pages]: Setup project, scaffold edge/main.go, write .env.
  • goflare auth: Save Cloudflare API token to the OS keyring.
  • goflare build: Infer mode from edge/main.go imports and produce artifacts.
  • goflare deploy: Direct Upload v2. ⚠️ Status not fully verified — primary deploy flow is git push via CF Git Integration.

edge/main.go:

//go:build wasm

package main

import (
    "github.com/tinywasm/goflare/pages"   // ← this import sets MODE=pages-functions
    "github.com/your-project/routes"
)

func main() {
    r := pages.NewRouter()
    routes.Register(r)
    pages.Serve(r)
}

Handlers live in modules/<feature>/handler.go and use the router.Context interface:

package contact

import (
    "github.com/tinywasm/goflare/cloudflare"
    "github.com/tinywasm/goflare/router"
)

func Handle(ctx router.Context) {
    ctx.SetHeader("Content-Type", "application/json")
    ctx.WriteStatus(200)
    ctx.Write([]byte(`{"ok":true}`))
    _ = cloudflare.Env("RESEND_API_KEY") // read secret from context.env
}

routes/routes.go wires URLs to handlers (build-agnostic — reused by edge/main.go and web/server.go):

package routes

import (
    "github.com/tinywasm/goflare/router"
    "github.com/your-project/modules/contact"
)

func Register(r router.Router) {
    r.Post("/api/contacto", contact.Handle)
}

Edge function — Workers (legacy)

edge/main.go:

//go:build wasm

package main

import "github.com/tinywasm/goflare/workers"

func main() { workers.Handle(handler) }

func handler(w *workers.Response, r *workers.Request) {
    w.Header()["Content-Type"] = "application/json"
    w.WriteHeader(200)
    w.Write([]byte(`{"ok":true}`))
}

D1 Database (Cloudflare D1)

Connect to a Cloudflare D1 SQLite database from within a Worker or Pages Function edge handler using github.com/tinywasm/goflare/d1. See docs/D1.md for full documentation.

db, err := d1.New("DB") // "DB" is the binding name in wrangler.toml
if err != nil { ... }
defer db.Close()
db.CreateTable(&MyModel{})
db.Create(&MyModel{...})

⚠️ Critical: NO heavy stdlib in wasm code

Files with //go:build wasm (everything under edge/, routes/, modules/, workers/, pages/pages.go, cloudflare/env_wasm.go) NEVER import fmt, strings, errors, encoding/*, net/http, log, io/ioutil. Use tinywasm/fmt, tinywasm/json, tinywasm/strings, tinywasm/fetch instead.

Stdlib inflates the wasm binary ~80% and exceeds Cloudflare Free's 1 MiB limit. TinyGo also does not fully support net/http in js/wasm.

Verification: grep -rE '^\s*"(fmt|strings|errors|encoding|net/http|log|io/ioutil)"' edge/ routes/ modules/ workers/ pages/pages.go cloudflare/env_wasm.go must return empty.

The dev local can use stdlib freely in web/server.go and pages/devserver/ — those don't run on the edge.

Frontend WASM

Minimal working web/client.go:

//go:build wasm

package main

import (
    "github.com/tinywasm/dom"
    "github.com/tinywasm/form"
)

type MyForm struct {
    Name string `input:"required"`
}

func main() {
    data := &MyForm{}
    f, _ := form.New("app", data)
    dom.Render("app", f)
    select {}
}

Note: compiled to web/public/client.wasm.

Shared models (modules/)

Shared models live in modules/ and can be imported by both frontend and edge.

package contact

// ormc:formonly
type ContactForm struct {
    Nombre  string `input:"required,min=2"`
    Email   string `input:"email,required"`
    Mensaje string `input:"textarea,required,min=10"`
}

Library usage

cfg := &goflare.Config{
    ProjectName: "myapp",
    AccountID:   "acc-id",
    PublicDir:   "web/public",
}
g := goflare.New(cfg)
g.Build()
store := goflare.NewKeyringStore()
g.Deploy(store)

Config reference

Field .env key Default Notes
ProjectName PROJECT_NAME required
AccountID CLOUDFLARE_ACCOUNT_ID required
WorkerName WORKER_NAME <ProjectName>-worker optional
Entry ENTRY auto: edge if edge/main.go exists directory name, not a file
PublicDir PUBLIC_DIR web/public static assets root
FunctionsDir FUNCTIONS_DIR functions Pages Functions output dir
Domain DOMAIN optional custom domain
CompilerMode COMPILER_MODE S S=small/prod, M=debug, L=Go std

At least one of Entry or PublicDir must be set. There is no MODE key — the mode is inferred from edge/main.go imports.

Build output

After goflare build:

Mode Output Committed?
Pages Functions functions/[[path]].mjs + functions/edge.wasm ✅ yes — CF Git Integration deploys them as-is
Workers (legacy) .build/edge.js + .build/edge.wasm ❌ no — .build/ is gitignored
Static Pages only web/public/ (produced by tinywasm framework, NOT by goflare) ✅ yes

Compiler modes

  • S: Small (TinyGo production, minified)
  • M: Medium (TinyGo debug)
  • L: Large (Go standard compiler)

Requirements

  • Go 1.25.2+
  • TinyGo — installed automatically by goflare build via tinywasm/tinygo

goflare calls tinygo.EnsureInstalled() before each Worker build. If TinyGo is already in PATH at the correct version, nothing happens. If it is missing or outdated, it is downloaded and installed to the local cache automatically.

To manage TinyGo independently:

import "github.com/tinywasm/tinygo"

tinygo.EnsureInstalled()
env := tinygo.GetEnv()

Reference project

See goflare-demo for a complete example.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RunAuth added in v0.2.3

func RunAuth(envPath string, in io.Reader, out io.Writer, reset bool, check bool) error

RunAuth runs the auth command.

func RunBuild added in v0.1.0

func RunBuild(envPath string, out io.Writer) error

RunBuild runs the build command.

func RunDeploy added in v0.1.0

func RunDeploy(envPath string, in io.Reader, out io.Writer) error

RunDeploy runs the deploy command.

func RunInit added in v0.1.0

func RunInit(envPath string, in io.Reader, out io.Writer) error

RunInit runs the init command.

func UpdateGitignore added in v0.1.0

func UpdateGitignore(dir string) error

UpdateGitignore reads .gitignore in dir. Appends .env and .build/ if not already present. Creates .gitignore if it does not exist.

func Usage added in v0.1.0

func Usage() string

Usage returns the usage string.

func WriteEnvFile added in v0.1.0

func WriteEnvFile(cfg *Config, path string) error

WriteEnvFile writes a .env file with all non-empty fields.

Types

type Config

type Config struct {
	// Project identity
	ProjectName string // PROJECT_NAME
	AccountID   string // CLOUDFLARE_ACCOUNT_ID
	WorkerName  string // WORKER_NAME  (default: ProjectName + "-worker")

	// Routing
	Domain string // DOMAIN (optional — custom domain for Pages)

	// Build inputs
	Entry     string // ENTRY      (path to main Go file, empty = Pages only)
	PublicDir string // PUBLIC_DIR (path to static assets, empty = Worker only)

	// Build output (not in .env — always .build/)
	OutputDir string // default: ".build/"

	// Pages Functions output (sibling to web/public/, committed to git)
	FunctionsDir string // default: "functions"

	// Compiler
	CompilerMode string // "S" | "M" | "L"  default: "S"
}

func Init added in v0.1.0

func Init(in io.Reader, out io.Writer) (*Config, error)

Init runs the interactive wizard and returns a populated Config.

func InitWithDir added in v0.2.6

func InitWithDir(in io.Reader, out io.Writer, baseDir string) (*Config, error)

InitWithDir runs the interactive wizard with a custom base directory.

func LoadConfigFromEnv added in v0.1.0

func LoadConfigFromEnv(path string) (*Config, error)

LoadConfigFromEnv reads a .env file and populates Config. Falls back to OS environment variables if .env path is empty or does not exist. Applies defaults after loading.

func (*Config) Validate added in v0.1.0

func (c *Config) Validate() error

type DeployResult added in v0.1.0

type DeployResult struct {
	Target string
	URL    string
	Err    error
}

DeployResult represents the result of a deployment to a target.

type Goflare

type Goflare struct {
	Config *Config // exported so CLI can read it after LoadConfigFromEnv

	BaseURL string
	// contains filtered or unexported fields
}

func New

func New(cfg *Config) *Goflare

New creates a new Goflare instance with the provided configuration

func (*Goflare) Auth added in v0.0.97

func (g *Goflare) Auth(store Store, in io.Reader) error

Auth implements token validation and keyring storage as a method.

func (*Goflare) Build added in v0.1.0

func (g *Goflare) Build() error

Build orchestrates the build pipeline as a method.

Mode is inferred from edge/main.go imports (D11):

  • pages-functions: edge/main.go imports github.com/tinywasm/goflare/pages → output functions/[path].mjs + functions/edge.wasm
  • workers: edge/main.go imports github.com/tinywasm/goflare/workers → output .build/edge.js + .build/edge.wasm (legacy)
  • pages (static): no edge/main.go but PublicDir exists → only static + optional frontend wasm

func (*Goflare) Change

func (h *Goflare) Change(newValue string, progress func(msgs ...any))

func (*Goflare) Deploy added in v0.1.0

func (g *Goflare) Deploy(store Store) error

func (*Goflare) DeployPages added in v0.0.97

func (g *Goflare) DeployPages(store Store) error

DeployPages uploads the Pages build output (from config.OutputDir) to Cloudflare Pages.

func (*Goflare) DeployWorker added in v0.0.99

func (g *Goflare) DeployWorker(store Store) error

func (*Goflare) GeneratePagesFiles

func (g *Goflare) GeneratePagesFiles() error

func (*Goflare) GenerateWorkerFiles

func (g *Goflare) GenerateWorkerFiles() error

func (*Goflare) GetToken added in v0.1.0

func (g *Goflare) GetToken(store Store) (string, error)

GetToken reads the token from the store without prompting.

func (*Goflare) Label

func (h *Goflare) Label() string

func (*Goflare) Logger added in v0.0.40

func (g *Goflare) Logger(messages ...any)

func (*Goflare) MainInputFileRelativePath

func (h *Goflare) MainInputFileRelativePath() string

MainInputFileRelativePath returns the relative path to the main input file This is used by devwatch to determine file ownership for Go files

func (*Goflare) Name

func (h *Goflare) Name() string

func (*Goflare) NewFileEvent

func (h *Goflare) NewFileEvent(fileName, extension, filePath, event string) error

NewFileEvent handles file change events for goflare This method is called by devwatch when a relevant file changes

func (*Goflare) SetCompilerMode

func (g *Goflare) SetCompilerMode(newValue string)

SetCompilerMode changes the compiler mode mode: "L" (Large fast/Go), "M" (Medium TinyGo debug), "S" (Small TinyGo production)

func (*Goflare) SetLog added in v0.0.40

func (g *Goflare) SetLog(f func(message ...any))

func (*Goflare) Shortcuts

func (h *Goflare) Shortcuts() []map[string]string

func (*Goflare) SupportedExtensions

func (h *Goflare) SupportedExtensions() []string

SupportedExtensions returns the file extensions that goflare monitors For edge workers, we primarily watch .go files

func (*Goflare) UnobservedFiles

func (h *Goflare) UnobservedFiles() []string

UnobservedFiles returns files that should be ignored by the file watcher These are output files generated by goflare that shouldn't trigger recompilation

func (*Goflare) Value

func (h *Goflare) Value() string

func (*Goflare) WriteSummary added in v0.1.0

func (g *Goflare) WriteSummary(out io.Writer, results []DeployResult)

WriteSummary formats and writes the deploy summary to out.

type KeyringStore added in v0.1.0

type KeyringStore struct{}

KeyringStore is the real implementation using go-keyring.

func NewKeyringStore added in v0.1.0

func NewKeyringStore() *KeyringStore

func (*KeyringStore) Get added in v0.1.0

func (s *KeyringStore) Get(key string) (string, error)

func (*KeyringStore) Set added in v0.1.0

func (s *KeyringStore) Set(key, value string) error

type MemoryStore added in v0.1.0

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is an in-memory Store exported for use by library consumers in tests. Safe for concurrent use.

func NewMemoryStore added in v0.1.0

func NewMemoryStore() *MemoryStore

func (*MemoryStore) Get added in v0.1.0

func (s *MemoryStore) Get(key string) (string, error)

func (*MemoryStore) Set added in v0.1.0

func (s *MemoryStore) Set(key, value string) error

type Mode added in v0.2.6

type Mode string

Mode represents the inferred project build mode.

const (
	ModeUnknown        Mode = ""
	ModeWorkers        Mode = "workers"
	ModePagesFunctions Mode = "pages-functions"
	ModePagesStatic    Mode = "pages"
)

type Store added in v0.0.99

type Store interface {
	Get(key string) (string, error)
	Set(key, value string) error
}

Store abstracts keyring access for testability.

Directories

Path Synopsis
cmd
goflare command
tests
web/main.go command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL