core

module
v0.0.0-...-0d68cb6 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: Apache-2.0

README

OpenNSW Core

A Go SDK for building single window systems — integrated service portals that orchestrate complex, multi-step government or enterprise workflows across multiple agencies and backend services.

What is a Single Window System?

A single window system provides citizens or businesses with one portal to complete end-to-end service applications (e.g. trade permits, licensing, consignment approvals) without navigating separate agency websites. Behind the scenes it:

  • Runs long-lived workflows that span days or weeks
  • Coordinates interactive tasks where humans submit forms, upload documents, or wait for agency review
  • Integrates payment gateways for fees and levies
  • Calls external agency services on the user's behalf
  • Renders dynamic UI driven by workflow state

This SDK provides all the infrastructure pieces to build such a system, while keeping your domain logic in your own application.

Packages

Package Purpose
artifact Versioned configuration registry — load workflow definitions, form schemas, and templates by ID (includes artifact/adapter, bridge adapters for domain types)
authn JWT validation, identity context injection, and HTTP middleware
authz Scope-based authorization middleware and predicates, decoupled from authn
configyaml Loads a YAML config file into a Go struct, resolving {{env:NAME}} / {{file:/path}} placeholders via secret.SecretRef
cors CORS HTTP middleware
database database/sql connection factory (PostgreSQL, MySQL, SQLite) with pooling and health checks
htmlgen Print-ready HTML document generation from a static template and structured data, with contextual escaping
httputil Shared HTTP response helpers for JSON payloads and correlation-ID-tagged API error bodies
json Packages for working with decoded JSON documents (includes json/jsonpointer, RFC 6901 JSON Pointer get/set)
notifications Multi-channel notification router (SMS, email) with pluggable providers
pagination Standard pagination envelope and query parameter parsing
payment Pluggable payment gateway orchestration with webhook processing and idempotency
remote Registry-based outbound HTTP client with pluggable auth (API key, Bearer, OAuth2)
secret Secret-bearing config values (SecretRef) resolved from env, file, or literal sources
shared Small, dependency-free helpers (deep copy, nested map access, config validation) used across other modules
storage File storage abstraction (local filesystem and AWS S3) with presigned URLs
taskflow Micro-interactive task orchestration — the core engine for human-in-the-loop steps
temporal Temporal client factory
trace HTTP request trace propagation and context-based trace ID correlation
uiprojector Zone-based, metadata-driven UI rendering from workflow state and business data
workflow JSON DSL-driven Temporal workflow graph interpreter
xmlgen XML document generation from a static template and structured data, with automatic escaping

Requirements

  • Go 1.26+
  • PostgreSQL, MySQL, or SQLite
  • Temporal server (for workflow and task orchestration)

Installation

go get github.com/OpenNSW/core

Architecture Overview

flowchart TD
    Portal["User Portal / Frontend"]

    subgraph server["Your HTTP Server · authn / authz / cors"]
        DS["Domain Services"]
        SR["payment webhooks · storage routes"]
    end

    subgraph macrowf["workflow — macro DAG (Temporal)"]
        WF["JSON graph · START · TASK · GATEWAY · SPLIT · END"]
    end

    subgraph microwf["taskflow.TaskManager (Temporal)"]
        TM["micro workflows · human-in-the-loop steps"]
    end

    subgraph pluginset["Step Plugins"]
        P["USER_INPUT · PAYMENT · EXTERNAL_REVIEW · API_CALL · custom"]
    end

    SS["payment · remote · notifications · storage"]
    AR[("artifact registry<br>workflow defs · task templates<br>form schemas · render templates")]

    Portal -->|HTTP| server
    DS -->|StartWorkflow| macrowf
    server -->|"GetTaskRenderInfo / CompleteTaskStep"| microwf
    macrowf -->|TASK node reached| microwf
    microwf --> pluginset
    pluginset -->|use| SS
    macrowf -. loads .-> AR
    microwf -. loads .-> AR

The two-tier workflow design is central to single window systems:

  1. Macro workflow (the workflow package) — a long-lived Temporal workflow that represents the full service application (e.g. an export permit). It moves through TASK nodes, GATEWAY nodes, and SPLIT nodes according to a JSON graph definition.
  2. Micro workflow (the taskflow package) — each TASK node spawns a short-lived Temporal workflow that presents one interactive step to the user (a form, a payment, an external review). When the user completes the step the micro workflow signals the macro workflow to continue.

Full Wiring Example

The following shows how to assemble the components into a working application (condensed from the reference implementation pattern):

func Build(ctx context.Context, cfg *Config) (*App, error) {
    // 1. Database — wrap the *sql.DB with GORM for the GORM-backed stores
    sqlDB, err := database.New(ctx, cfg.Database)
    if err != nil {
        return nil, err
    }
    db, err := gorm.Open(postgres.New(postgres.Config{Conn: sqlDB}), &gorm.Config{})
    if err != nil {
        return nil, err
    }

    // 2. Artifact registry — one loader is the single source of truth
    artifactLoader, _ := local.New(local.Config{Root: "configs"})
    registry := artifact.NewRegistry(artifactLoader)
    manifest, _ := artifact.LoadManifest(ctx, artifactLoader) // reads configs/manifest.json
    artifact.RegisterFromConfig(registry, manifest)

    // 3. Payment
    paymentRepo := NewPaymentRepo(db)
    paymentRegistry, _ := payment.NewRegistry("configs/payment_methods.json", map[string]payment.Factory{
        "govpay": govpay.NewGovPayGateway,
    })
    paymentService := payment.NewPaymentService(paymentRepo, paymentRegistry)

    // 4. Temporal client
    temporalClient, _ := temporal.NewClient(cfg.Temporal)

    // 5. Task subsystem
    taskStore := gormstore.New(db)
    pluginRegistry := plugins.NewRegistry()
    pluginRegistry.Register("USER_INPUT",      plugins.NewUserInputPlugin())
    pluginRegistry.Register("PAYMENT",         NewPaymentPlugin(paymentService))
    pluginRegistry.Register("EXTERNAL_REVIEW", NewExternalReviewPlugin(remoteManager))
    pluginRegistry.Register("MY_PLUGIN",       &MyPlugin{})

    assembler, _ := uiprojector.NewAssembler(templateProvider, uiprojector.DefaultProjectors())
    taskRenderer := zoneview.NewTaskRenderer(assembler)

    var tm *orchestrator.TaskManager
    microRunner := workflow.NewTemporalManager(
        temporalClient, cfg.Temporal.Namespace, "MICRO_WORKFLOW_QUEUE",
        func(p workflow.TaskPayload) (map[string]any, error) { return tm.StartTaskStep(ctx, p) },
        func(c workflow.WorkflowCompletion) error { return tm.HandleTaskCompletion(ctx, c) },
    )
    onTaskCompleted := func(parentWorkflowID, parentStepID string, vars map[string]any) error {
        return consignmentService.HandleTaskCompletion(ctx, parentWorkflowID, parentStepID, vars)
    }
    tm = orchestrator.NewTaskManager(taskStore, registry, pluginRegistry, microRunner, onTaskCompleted, taskRenderer)
    microRunner.StartWorker()

    // 6. Macro workflow runner
    macroRunner := workflow.NewTemporalManager(
        temporalClient, cfg.Temporal.Namespace, "INTERPRETER_TASK_QUEUE",
        onMacroTaskActivation, onMacroCompletion,
    )
    macroRunner.StartWorker()

    // 7. Auth
    authnManager, _ := authn.NewManager(userProfileSvc, cfg.Authn)
    authzr, _ := authz.New(func(ctx context.Context) (authz.Principal, bool) {
        ac := authn.GetAuthContext(ctx)
        return ac, ac != nil
    })

    // 8. HTTP
    mux := http.NewServeMux()
    withAuth := authnManager.RequireAuthMiddleware()
    mux.Handle("GET /api/v1/tasks/{id}",  withAuth(authzr.RequireScope("tasks:read")(getTaskHandler(tm))))
    mux.Handle("POST /api/v1/tasks/{id}", withAuth(authzr.RequireScope("tasks:write")(submitTaskHandler(tm))))
    mux.Handle("POST /api/v1/payments/{gatewayId}/webhook", webhookHandler(paymentService))

    return &App{Handler: cors.CORS(cfg.CORS)(mux)}, nil
}

Contributing

Contributions are welcome. See CONTRIBUTING.md for local setup, hooks, the Apache-2.0 license-header policy, and commit conventions.

After cloning, install tools and enable git hooks in one step:

make setup

License

See LICENSE.

Directories

Path Synopsis
loaders
Package loaders selects and constructs the core/artifact Loader used to fetch artifacts (workflow definitions, templates, manifests) at startup.
Package loaders selects and constructs the core/artifact Loader used to fetch artifacts (workflow definitions, templates, manifests) at startup.
loaders/github
Package github provides an artifact Loader that fetches bytes from a GitHub repository over the REST Contents API, using only net/http (no GitHub SDK dependency).
Package github provides an artifact Loader that fetches bytes from a GitHub repository over the REST Contents API, using only net/http (no GitHub SDK dependency).
loaders/s3
Package s3 provides an artifact Loader that reads objects from an S3 bucket (or an S3-compatible store).
Package s3 provides an artifact Loader that reads objects from an S3 bucket (or an S3-compatible store).
testutil
Package testutil provides shared test helpers for packages that work with the artifact registry.
Package testutil provides shared test helpers for packages that work with the artifact registry.
authn module
authz module
configyaml module
database module
htmlgen module
httputil module
json module
pagination module
payment module
refid module
remote module
secret module
shared module
storage module
taskflow
callbacktoken
Package callbacktoken encodes the address of one step of a task into the opaque token a plugin sends to an external system, and decodes it when the system calls back.
Package callbacktoken encodes the address of one step of a task into the opaque token a plugin sends to an external system, and decodes it when the system calls back.
trace module
uiprojector module
Package engine implements a Temporal-based graph interpreter workflow engine.
Package engine implements a Temporal-based graph interpreter workflow engine.

Jump to

Keyboard shortcuts

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