Help us shape the future of Go! Take the 2026 Go Developer Survey

server

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 25 Imported by: 0

README

Standalone ContentKit

The server runs ContentKit behind an AuthKit resource server. engine is the same configuration accepted by the embedded engine; secrets come from the environment as listed in config.go.

Scoped authorization

Scoped deployments bind the catalog to a persisted tenant and app, and bind authorization to existing AuthKit groups. For example:

engine:
  scope:
    tenant_id: 11111111-1111-4111-8111-111111111111
    app_id: 22222222-2222-4222-8222-222222222222
  database:
    schema: example_catalog
  accounts:
    schema: example_accounts
  content:
    languages: [en]
    kinds:
      gallery: {route: g, presentation: reader, versions: {languages: one}}
auth:
  database:
    schema: example_auth
  token:
    issuer: https://contentkit.example.test
    issued_audiences: [contentkit]
  resource:
    id: https://contentkit.example.test
authorization:
  issuer: https://accounts.example.test
  app_group_id: 22222222-2222-4222-8222-222222222222
  tenant_group_id: 11111111-1111-4111-8111-111111111111

The IDs are examples. Generate them once and use the same canonical tenant UUID in AuthKit and ContentKit. The server derives AuthKit's default tenant from engine.scope.tenant_id when absent and rejects a conflicting explicit value. Provision the tenant before startup. app_group_id must equal the app UUID and name a live app group owned by that tenant. The optional tenant_group_id must equal the tenant UUID and name its built-in tenant group. Startup does not create groups or assign roles.

Local accounts use AuthKit's live tenant ownership. Root operators may administer other tenants with native effective permissions while retaining their source identity; this does not create an audience account there. app:staff grants apply to the app, and tenant:staff uses the same app permission objects across that tenant's apps. Shared avatar administration is tenant:avatars:write. A tenant-bound user directory validates avatar targets.

authorization.issuer is the verified identity issuer; auth.token.issuer is the local permission authority. For an external identity issuer, provision its enabled remote application in app_group_id, with explicitly restricted permissions and public verification keys. This remote compatibility path requires a dedicated one-tenant issuer; it cannot attest ownership for a shared multi-tenant account directory. Local shared AuthKit setups use the native verified tenant resolver instead.

Existing contentkit and contentkit-tenant personas require an explicit AuthKit migration preserving IDs, assignments and credential bounds before startup; see the authorization contract.

Legacy configurations without engine.scope retain the existing root staff policy and trusted-root directory behavior. They must omit the authorization section. Adopting existing data requires the engine's explicit scope/account migration procedure; changing a namespace or regenerating IDs is not an adoption strategy.

Documentation

Overview

Package server is ContentKit as a standalone deployment: the engine behind its own HTTP server, an AuthKit resource server deciding who each request is, the trusted issuer's directory naming authors, and OpenRails' remote client answering entitlements.

Index

Constants

View Source
const StaffRole = "staff"

StaffRole names the declared ContentKit staff role. Legacy deployments use root:staff; scoped deployments use app:staff and native inherited authority.

Variables

View Source
var Secrets = []struct {
	Env  string
	Fill func(*Config, string)
}{
	{"CONTENTKIT_MEDIA_TOKEN_KEY", func(c *Config, v string) { media(c).Delivery.SigningKey = v }},
	{"CONTENTKIT_MEDIA_TOKEN_PREVIOUS_KEY", func(c *Config, v string) { media(c).Delivery.PreviousKey = v }},
	{"CONTENTKIT_S3_ACCESS_KEY_ID", func(c *Config, v string) { c.S3.AccessKeyID = v }},
	{"CONTENTKIT_S3_SECRET_ACCESS_KEY", func(c *Config, v string) { c.S3.SecretAccessKey = v }},
	{"CONTENTKIT_CLICKHOUSE_PASSWORD", func(c *Config, v string) { c.Engine.ClickHouse.Password = v }},
	{"CONTENTKIT_OPENRAILS_API_KEY", func(c *Config, v string) { c.OpenRails.APIKey = v }},
}

Secrets are read from the environment, never the config file: each variable, when set, fills its field.

Functions

This section is empty.

Types

type AuthorizationConfig added in v0.3.0

type AuthorizationConfig struct {
	Issuer        string `yaml:"issuer"`
	AppGroupID    string `yaml:"app_group_id"`
	TenantGroupID string `yaml:"tenant_group_id"`
}

AuthorizationConfig selects existing grants; startup never creates groups or assigns their roles. Local identity uses AuthKit's verified tenant account ownership. An explicitly remote issuer retains the legacy dedicated-directory contract until its tenant-aware identity integration is configured.

type Config

type Config struct {
	// Engine is what contentkit.New takes.
	Engine contentkit.Config `yaml:"engine"`
	// Auth is the server's AuthKit as a resource server (AuthKit's own
	// configuration): Token.Issuer, Resource.ID and the site's trusted
	// issuer in RemoteApplications. It mints nothing unless Keys says so;
	// its tables live in Database.Schema (default "contentkit_auth").
	Auth authkit.Config `yaml:"auth"`
	// Authorization binds a scoped engine to explicitly provisioned AuthKit
	// groups and a verified tenant identity directory. Required with Scope.
	Authorization *AuthorizationConfig `yaml:"authorization"`
	// UsersIssuer is the trusted issuer whose directory names authors. Legacy
	// mode can infer the only RemoteApplication; scoped mode uses Authorization
	// and refuses a conflicting UsersIssuer.
	UsersIssuer string `yaml:"users_issuer"`
	// OpenRails is where entitlements are read; an empty BaseURL runs
	// without billing.
	OpenRails OpenRailsConfig `yaml:"openrails"`
	// S3 is the media bucket, required with Engine.Media.
	S3 S3Config `yaml:"s3"`
	// Routes are the route groups served (contentkit.ParseRouteGroups names);
	// empty is every group the engine can serve.
	Routes []string `yaml:"routes"`
	// Prefix is where the API is served: "/api/contentkit" serves
	// /api/contentkit/v1/*. Empty is the root.
	Prefix string `yaml:"prefix"`
	// Addr is where Run listens; empty is ":8080".
	Addr string `yaml:"addr"`
	// DrainDelay is how long Run keeps serving once ctx ends, with /readyz
	// failing, so load balancers stop routing here first.
	DrainDelay time.Duration `yaml:"drain_delay"`
	// ShutdownTimeout bounds finishing in-flight requests; 0 is 30s.
	ShutdownTimeout time.Duration `yaml:"shutdown_timeout"`
}

Config is a standalone deployment: plain data. Secrets are read from the environment into it (cmd/contentkit).

func Load

func Load(raw []byte, getenv func(string) string) (Config, error)

Load reads a YAML config file, refusing unknown fields, then fills the secrets from getenv.

type Deps

type Deps struct {
	// Postgres holds ContentKit's, River's and AuthKit's schemas. Required.
	Postgres *pgxpool.Pool
	// Redis is optional: shared rate limits and DPoP replay state.
	Redis  redis.UniversalClient
	Logger *slog.Logger
}

Deps is what the server reaches outside its process.

type OpenRailsConfig

type OpenRailsConfig struct {
	BaseURL string `yaml:"base_url"`
	// APIKey is the merchant's API key (a secret).
	APIKey string `yaml:"api_key"`
}

OpenRailsConfig is the OpenRails server holding the site's merchant.

type S3Config

type S3Config struct {
	Bucket string `yaml:"bucket"`
	Region string `yaml:"region"`
	// Endpoint is where the server reaches the bucket; PublicEndpoint where
	// browsers PUT presigned uploads (default Endpoint).
	Endpoint       string `yaml:"endpoint"`
	PublicEndpoint string `yaml:"public_endpoint"`
	UsePathStyle   bool   `yaml:"use_path_style"`
	// AccessKeyID and SecretAccessKey are secrets.
	AccessKeyID     string `yaml:"access_key_id"`
	SecretAccessKey string `yaml:"secret_access_key"`
}

S3Config is the media bucket.

type Server

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

Server is a standalone ContentKit.

func New

func New(ctx context.Context, cfg Config, d Deps) (_ *Server, err error)

New builds the server: AuthKit (which creates or upgrades its tables), then the engine (its own), one River fleet in the engine's River schema running both, and the HTTP surface. ctx bounds the wait for the database.

func (*Server) Close

func (s *Server) Close(ctx context.Context) error

Close stops the jobs and closes the engine and AuthKit, leaving the pool and Redis open.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler is the public surface: ContentKit's routes and /livez, /readyz, /statusz.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

Run starts the server and serves Config.Addr until ctx ends, then drains: /readyz fails for DrainDelay, in-flight requests finish within ShutdownTimeout, and the jobs stop.

func (*Server) Serve

func (s *Server) Serve(ctx context.Context, ln net.Listener) error

Serve is Run on a listener.

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

Start runs the jobs (AuthKit's and ContentKit's on one fleet) and marks the server ready once the engine is.

Directories

Path Synopsis
cmd
contentkit command
Command contentkit is ContentKit as its own server (package server): the engine's routes behind an AuthKit resource server, entitlements from OpenRails, health at /livez, /readyz and /statusz.
Command contentkit is ContentKit as its own server (package server): the engine's routes behind an AuthKit resource server, entitlements from OpenRails, health at /livez, /readyz and /statusz.

Jump to

Keyboard shortcuts

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