Files
summercms/modules/pact
Jakub Zych ccf77b9103 test(19.7-09): named tests for every framework and admin SPA fix of plans 06 and 07
- pier: TestSessionReserve (A-WR-03), TestLockScope (A-WR-07),
  TestScopeWidening (A-WR-04), TestStatelessFanOut (A-WR-05, A-WR-06),
  TestPersonalTokenBinding (A-WR-09), TestDispatchHardening (A-WR-01,
  A-WR-02, A-WR-08, A-IN-04, A-IN-05, A-IN-08), TestCatalogPathFields
  (A-WR-02, A-IN-07), TestPromptWriteCeiling (C-IN-01),
  TestTokenScopedRevoke (B-WR-04, C-IN-02), TestResolveClientAddress
  (B-IN-04); test seam WrapStore
- pact TestFrontendTokenScopes, towel TestClientIP, cabana
  TestTokenScopeNameParity
- cabana TestAccountContributionURL (B-WR-03), TestAccountContributionsIsolation
  (B-WR-06, B-IN-03), TestRecordActionPromptLocalized (B-IN-02), and the
  Postgres TestAccountTokenRevokeEvent (B-WR-04) and
  TestResolveBackendTokenRemote (B-IN-04)
- vitest: hotkeysEditing.test.ts (B-WR-01, B-WR-02) and the extended
  AccountContributions.test.ts (B-WR-03, B-WR-06, B-IN-01)
- check-phase19.7.sh requires every name (GO_TESTS, GO_TAGGED_TESTS,
  DOCKER_TESTS, ADMIN_FILES) and scans the new files (PHASE_TEST_FILES)
2026-10-23 18:36:16 +02:00
..

pact

Capability interfaces that compiled plugins implement to contribute routes, config, migrations, middleware, commands, admin screens, translations, mail templates, jobs and scheduled commands.

import "git.golem15.com/golem15/summercms/modules/pact"

Name: An agreement both sides keep; it holds the interfaces a plugin agrees to implement.

Overview

pact is the contract layer between plugins and the framework. It holds interfaces and plain data types, with no behaviour of its own. A plugin opts into a capability by implementing one of the Has* interfaces, and the framework package that owns the capability discovers it with a type assertion (party for config, surf for routes and middleware, lagoon for migrations, cabana for admin controllers, conga for jobs and schedules). It replaces the register*() methods of a WinterCMS PluginBase (registerPermissions, registerNavigation, registerSettings and so on) with small, separately implementable interfaces.

Role in the framework

The contract layer between the framework and plugins; it depends only on bonfire. party, lagoon, conga, cabana, phrasebook, postcard, lighthouse and surf all read it. It holds interfaces only, so a plugin can depend on the contracts without pulling in the modules that implement them.

Use cases:

  • Make a plugin contribute routes, migrations, commands and admin screens by implementing the matching interfaces.
  • Offer optional features: a plugin implements only the capabilities it needs.
  • Write plugins that are compiled in without a runtime loader, checked by the compiler.

Features

  • Plugin capability interfaces: pact.HasRoutes, pact.HasConfig, pact.HasMigrations, pact.HasCommands, pact.HasModels, pact.HasJobs, pact.HasSchedule, pact.HasLang, pact.HasLangOverrides and pact.HasMailTemplates.
  • HTTP contracts: the pact.Router group builder (implemented by surf), the pact.Middleware type, and named, parameterized (name:param) and house-envelope middleware through pact.HasMiddleware, pact.HasMiddlewareFactories and pact.HasHouseMiddleware.
  • Backend registration data: pact.Permission, pact.NavigationItem and pact.SettingsItem, exposed through pact.HasPermissions, pact.HasNavigation and pact.HasSettings. A settings entry is either a singleton form or a link that opens an admin controller from the Settings page. A pact.NavigationItem.Visible func hides a navigation entry at request time (nil = always listed).
  • Personal API token scopes: pact.TokenScope and pact.HasTokenScopes, so a plugin declares named scopes (permissions and HTTP methods) that administrators can assign when creating a personal API token.
  • Member token scopes: pact.FrontendTokenScope and pact.HasFrontendTokenScopes, the frontend counterpart of pact.HasTokenScopes, so a plugin declares scopes for the user plugin's member personal API tokens; the admin token catalog never reads them. pact.ValidTokenScopeName is the one scope-name rule both catalogs apply.
  • Frontend permission codes: pact.FrontendPermission and pact.HasFrontendPermissions, the frontend counterpart of pact.HasPermissions, so a plugin guarding a route with a permission code also declares that code for the user plugin's permission editors.
  • MCP declarations, free of any MCP SDK: pact.HasMCP with pact.MCP, pact.MCPTool and pact.MCPPrompt bind tools and prompts to the plugin's own token-guarded routes; pact.MCPGuard and pact.MCPGuards let the owner of a route guard publish how its routes are guarded and dispatched; pact.MCPTokenVerifier, pact.MCPPersonalTokenResolver and pact.MCPLinkedBackendResolver are the services the MCP endpoint looks up; pact.MCPSubjectChanged is the event that refreshes a session's tool list.
  • My account contributions: pact.HasAccountContributions with pact.AccountContribution and pact.AccountAction, so a plugin adds a section with a status line and buttons to the administrator's own account page, run for the signed-in administrator only.
  • Admin controller contracts: pact.AdminController, pact.HasAdminControllers, pact.AdminAssets (embedded Winter-shaped admin YAML), pact.AdminPermissioned and pact.AdminRecordSource.
  • Admin extension contracts, so a plugin extends the compiled admin SPA without a Node build: pact.AdminClientAssets (per-controller JS and CSS from the plugin's embedded assets/ tree, Winter's addJs/addCss), pact.HasAdminActions with pact.AdminAction, pact.AdminActionInput and pact.AdminActionResult (named toolbar and widget actions whose routes, CSRF check, permissions and record scoping the framework owns), pact.HasAdminBulkActions with pact.AdminBulkAction, pact.AdminBulkActionInput and pact.AdminBulkActionResult (named actions on the rows selected in a list, which receive records the framework loaded through the list scope, never ids; a Detached action runs outside the framework transaction), pact.HasAdminRecordActions with pact.AdminRecordAction, pact.AdminRecordActionInput and pact.AdminRecordActionResult (named actions on one record, each with an Applies rule for the record's state), pact.HasAdminActionExtensions with pact.AdminActionExtension (a plugin adds record actions to any admin controller by controller ID, one it does not own included; they are offered on that controller's preview screen after the owner's), and pact.AdminPartialData (the curated view model a partial template renders).
  • Plugin field types: pact.HasFormFieldTypes returns pact.FormFieldType entries, admin form field types any controller's fields.yaml uses by a short alias (type: notes). A type names its custom element, its scripts and styles, its own option keys and its Go funcs: Decode turns a submitted pact.FormFieldValue into a pact.FormFieldDecoded (the value to store plus optional pact.FormFieldNotice messages for the administrator, such as what a sanitizer removed) or refuses it with a pact.FormFieldError; Load serves the stored value to the element, ListValue formats a list column, and Upload stores a file from a pact.FormFieldUpload and answers a pact.FormFieldUploadResult. The admin framework owns the routes, permissions, record scope, locale storage of a translatable type and the localization of notices.
  • Optional admin hooks a controller or model can implement: list and form query scoping (pact.ListExtendQuery, pact.FormExtendQuery), list row states (pact.ListRowStates with the fixed pact.RowState set pact.RowStateDeleted, pact.RowStateNegative and pact.RowStateDisabled), values of unstored list columns filled once per page (pact.ListExtendRecords), the record title of the form and preview screens (pact.FormRecordTitle), create, update and delete hooks (pact.FormBeforeCreate, pact.FormAfterUpdate, pact.FormBeforeDelete and their siblings), a hook after a reorder save (pact.ReorderAfterSave), form-only fields that reach those hooks without being model columns (pact.FormVirtualFields), validation rules per operation for admin saves (pact.FormRules), relation hooks (pact.RelationExtendManageQuery, pact.RelationExtendOptionsQuery, pact.RelationBeforeLink), relation child hooks around creating, updating and deleting a related record (pact.RelationBeforeCreate, pact.RelationAfterCreate, pact.RelationBeforeUpdate, pact.RelationAfterUpdate, pact.RelationBeforeDelete, pact.RelationAfterDelete), filter scopes (pact.FilterScope, pact.FilterOptions) and dropdown options (pact.DropdownOptionsProvider).
  • Dependent dropdown options: pact.DependentOptionsProvider answers a pact.DependentOptionSet (whether the sibling's value takes a choice at all, and the choices) for a dropdown with dependsOn, from the submitted values of its siblings and with a context, so the choices can come from the database; the admin framework calls it for the options route and again on every save. pact.Option.Depth indents a choice taken from a tree.
  • Menu item types: pact.HasMenuItemTypes returns pact.MenuItemType values (key, label, default URL pattern, PatternFrom for a list type, the References an item can point at as a pact.MenuItemReference tree, and Resolve), the typed successor of WinterCMS's pages.menuitem.* events. Resolve receives a pact.MenuItemResolve with the pattern already layered and answers a pact.ResolvedMenuItem with its alternates as an ordered []pact.MenuItemAlternate. signpost collects them at boot.
  • Singleton admin controllers: pact.AdminSingleton (SingletonRecord, first or create) makes a controller edit exactly one record, opened directly instead of from a list.
  • A background job contract (pact.Job, pact.JobArgs) that does not depend on any queue library.
  • A schedule contract: pact.HasSchedule returns pact.ScheduledCommand entries (a registered command name, its arguments and a pact.Cadence built with pact.Daily, pact.DailyAt or pact.Every), the Go form of WinterCMS registerSchedule. It does not depend on any queue library either.
  • pact.OptionalMessage, a service an optional plugin can publish so others integrate with it without importing its package.
  • pact.HasModels and pact.OptionalMessage are declared for plugins to implement, but no framework package consumes them yet.

Usage

A plugin declares its capabilities by implementing the interfaces and asserting them at compile time:

package blog

import (
	"net/http"

	"git.golem15.com/golem15/summercms/modules/pact"
)

var (
	_ pact.HasRoutes      = (*Plugin)(nil)
	_ pact.HasPermissions = (*Plugin)(nil)
)

type Plugin struct{}

func (p *Plugin) Routes(r pact.Router) error {
	r.Group("/api/blog", []string{"auth"}, func(r pact.Router) {
		r.Get("/posts", listPosts)
		r.Get("/posts/{id}", showPost)
		r.Where("id", "[0-9]+")
	})
	return nil
}

func (p *Plugin) Permissions() []pact.Permission {
	return []pact.Permission{
		{Code: "acme.blog.access_posts", Tab: "Blog", Label: "Manage posts"},
	}
}

func listPosts(w http.ResponseWriter, r *http.Request) {}
func showPost(w http.ResponseWriter, r *http.Request)  {}

A plugin schedules one of its registered commands by implementing pact.HasSchedule:

var _ pact.HasSchedule = (*Plugin)(nil)

func (p *Plugin) Schedule() []pact.ScheduledCommand {
	return []pact.ScheduledCommand{
		{Command: "blog:prune-drafts", Cadence: pact.Daily()},
		{Command: "blog:ping", Args: []string{"--quiet"}, Cadence: pact.Every(15 * time.Minute)},
	}
}

API reference

Identifier Description
pact.Router Laravel-style route group builder (pact.Router.Group, pact.Router.GroupRaw, one method per HTTP verb, pact.Router.Where, pact.Router.WhereIn); implemented by surf.
pact.HasRoutes Declares HTTP routes on a pact.Router.
pact.Middleware A named func(http.Handler) http.Handler wrapper.
pact.HasMiddleware Registers named middleware.
pact.HasMiddlewareFactories Registers parameterized middleware resolved from name:param at wrap time.
pact.HasHouseMiddleware Registers middleware tagged as envelope and error handling, which raw groups refuse.
pact.HasConfig Ships default YAML config, merged under the plugin ID.
pact.HasMigrations Ships an ordered gormigrate set, run with a per-plugin history table.
pact.HasCommands Contributes bonfire console commands to the application binary.
pact.HasModels Exposes GORM models.
pact.Job Background unit of work that receives pact.JobArgs.
pact.HasJobs Registers background jobs.
pact.HasSchedule Declares recurring console commands (Schedule() []pact.ScheduledCommand); conga workers run them.
pact.ScheduledCommand One schedule entry: Command, Args and Cadence.
pact.Cadence Opaque run frequency with IsZero, Interval (24h for daily cadences) and At (hour and minute of a daily cadence).
pact.Daily Cadence at 00:00 every day in the app timezone (Laravel ->daily()).
pact.DailyAt Cadence at a given hour and minute every day in the app timezone.
pact.Every Cadence at every multiple of an interval since local midnight; the interval must be at least 1s and divide 24h.
pact.HasLang Ships translation YAML under lang/<locale>/<group>.yaml.
pact.HasLangOverrides Replaces or adds translations of any loaded namespace, including the framework's own.
pact.HasMailTemplates Ships mail templates and layout aliases.
pact.Permission One backend permission entry (code, tab, label, roles).
pact.HasPermissions Declares backend permissions.
pact.TokenScope One personal API token scope: name, label, description, permission patterns and HTTP methods.
pact.HasTokenScopes Declares personal API token scopes compiled at admin boot.
pact.FrontendTokenScope One member personal API token scope: Name (what a route's user.scope:<name> middleware checks), Label and Description.
pact.HasFrontendTokenScopes Declares member token scopes, the frontend counterpart of pact.HasTokenScopes; compiled by the plugin that owns member tokens, never by the admin token catalog.
pact.ValidTokenScopeName Reports whether a scope name is valid (^[a-z][a-z0-9_-]*(:[a-z][a-z0-9_-]*)?$); shared by both token catalogs.
pact.NavigationItem One backend navigation entry, with an optional side menu and an optional Visible func the admin runs on every navigation request.
pact.SettingsItem One settings screen entry: a singleton settings form (Model, Form, NewModel), or a link to an admin controller when Controller names its ID, as a WinterCMS 'url' => Backend::url(...) entry.
pact.AdminController Admin controller identity: ID, model name and YAML config directory.
pact.AdminAssets Embedded tree of the plugin's admin YAML.
pact.AdminRecordSource Supplies a new model record for the generic admin handlers.
pact.AdminClientAssets Declares a controller's admin JS (AdminJS) and CSS (AdminCSS) files, paths under the plugin's assets/ directory.
pact.AdminAction One named controller action: name, label, extra permissions and the Go Run function.
pact.AdminActionInput What an action receives: widget field, optional record id and scoped record, the fill snapshot, and the widget's raw JSON payload (Payload, at most 64 KiB, nil when absent, never set for a toolbar action, not inspected by the framework).
pact.AdminActionResult What an action returns: a message for the toast, the fill write-back values, and an optional Data value passed through to the widget as data (any JSON up to 256 KiB encoded, not filtered like fill, omitted when nil).
pact.HasAdminActions Registers a controller's actions for toolbar.buttons and type: widget fields.
pact.AdminBulkAction One named bulk action: name, label, optional confirm text, extra permissions, the Go Run function and Detached (run outside the framework transaction, records loaded without row locks, for actions that call an external service).
pact.AdminBulkActionInput What a bulk action receives: Records, the selected records loaded and row-locked through the list scope (without row locks for a Detached action).
pact.AdminBulkActionResult What a bulk action returns: an optional message for the toast and Affected, the number of records it changed.
pact.HasAdminBulkActions Registers a controller's bulk actions for the bulkActions list of config_list.yaml.
pact.AdminRecordAction One named record action: name, label, optional confirm text, extra permissions, the Danger flag (the admin confirms it in the danger variant), an optional Prompt (the confirm dialog asks for one text value), the optional Applies rule and the Go Run function.
pact.AdminActionPrompt The text input of a prompted record action: Label (required), Placeholder, Comment (phrase keys or text) and MaxLength in runes (0 means 255).
pact.AdminRecordActionInput What a record action receives: RecordID and Record, the record loaded and row-locked through the form scope, and Value, the trimmed prompt answer (empty without a Prompt).
pact.AdminRecordActionResult What a record action returns: an optional message for the toast.
pact.HasAdminRecordActions Registers a controller's record actions for the recordActions list of config_form.yaml.
pact.AdminActionExtension Record actions a plugin adds to one admin controller by its Controller ID; offered on the target's preview screen after the owner's actions, each checked against the target's RequiredPermissions and its own Permissions. A contribution to summercms.cabana.users is refused with user.outranked when the target administrator outranks the caller.
pact.HasAdminActionExtensions Implemented by a plugin (not a controller) that adds actions to admin controllers, its own or another plugin's. An unknown controller ID, a target without a preview block or a duplicate name stops the boot.
pact.HasAccountContributions Implemented by a plugin that adds sections to the admin's My account page.
pact.AccountContribution One My account section: Name (an identifier unique across plugins), Label, Description, an optional pure-read Status func for the signed-in administrator and its Actions. A non-identifier or taken name, an empty label, a duplicate or non-identifier action name, a nil Run or an empty action label stops the boot.
pact.AccountAction One button of a section: name, label, optional Confirm text, Danger, extra Permissions, an optional pure-read Applies rule and the Go Run function, which acts for the signed-in administrator only.
pact.AccountActionInput What an account action receives: AdminID, the signed-in administrator, and nothing else.
pact.AccountActionResult What an account action returns: a Message for the toast and an optional URL the admin opens in a new tab.
pact.HasMCP Implemented by a plugin that declares MCP tools and prompts for the framework's MCP endpoint.
pact.MCP The declaration: Tools and Prompts (resources are a later field of the same struct).
pact.MCPTool One tool bound to one of the plugin's own token-guarded routes by Method and Path: unprefixed Name, static Title and Description, the Guard it runs as, the Input struct (zero value; path, query, json and jsonschema tags map and describe the arguments), an optional Output struct, extra Permissions for the listing and the ReadOnly, Destructive, Idempotent and OpenWorld overrides of the annotations derived from the method (nil means derived).
pact.MCPPrompt One prompt template: name, title, description, guard, permissions, Arguments (pact.MCPPromptArgument), the Render func returning pact.MCPPromptMessage values (Role user or assistant, Text) and Writes, set when the prompt directs tools that are not read-only so it is listed only for an identity that may write (zero value: offered to read-only grants too).
pact.MCPGuard A guard provider the owner of a route guard publishes: its name, whether a middleware list is token-guarded by it, the scopes and permissions a middleware list requires (pact.MCPRequirements), whether a subject may call a tool, and the pre-resolved principal for one in-process call (pact.MCPDispatch).
pact.MCPGuards The registry of guard providers published on the application; pact.NewMCPGuards creates it, Register refuses an empty or duplicate name with a pact: error naming the guard, Lookup finds one and Names lists them sorted. Safe for concurrent use.
pact.MCPSubject The identity an MCP session acts as under one guard: guard name, user id, granted scopes, OAuth client id and personal token id.
pact.MCPDispatch A guard provider's answer for one call: the principal (as any), an optional credential and the bouncer guard names that honour the dispatch marker.
pact.MCPTokenVerifier Published by the OAuth server plugin: verifies an access token at the MCP endpoint into a pact.MCPGrant (user, client, token, scopes, expiry) and names its resource. A refused token is pact.ErrMCPTokenInvalid.
pact.MCPPersonalTokenResolver Published by the one plugin whose personal tokens may open an MCP session (one resolver per application): resolves a raw token into a pact.MCPSubject whose TokenID identifies the token row (it keys the session binding; a subject without one is refused); false when the prefix is not its own, which the endpoint answers as unauthorized.
pact.MCPLinkedBackendResolver Published by the plugin that owns the frontend-to-backend account link: the pact.LinkedBackendUser (id, login, display name) of a frontend user, false when unlinked.
pact.MCPSubjectChanged Event fired on the festival bus when a subject's permissions, link, grants or existence change (Kind is pact.MCPSubjectPermissions, pact.MCPSubjectLink, pact.MCPSubjectRevoked or pact.MCPSubjectDeleted), so the MCP endpoint re-lists tools or ends sessions. TokenID and ClientID narrow a revoked event: a token id ends only the sessions that personal token opened (subject with that TokenID and no ClientID), a client id only that OAuth client's sessions, neither every session of the subject; other kinds ignore them.
pact.FrontendPermission One frontend permission code a plugin declares: Code, Label, Tab and Comment, offered in the user plugin's permission editors next to the seeded codes.
pact.HasFrontendPermissions Declares frontend permission codes, the frontend counterpart of pact.HasPermissions.
pact.ReorderAfterSave Optional controller hook ReorderAfterSave(ctx, partition): runs after a reorder save wrote the new order, inside the same transaction (cabana.TxFromContext works); an error rolls the reorder back.
pact.AdminPartialData Supplies the view model a controller partial template renders; never the GORM model.
pact.HasFormFieldTypes Implemented by a plugin (not a controller) that registers admin form field types.
pact.FormFieldType One field type: the alias Name, the custom-element tag Element, Translatable, the Scripts and Styles under assets/, the Options keys, and the funcs CheckOptions (optional), Decode (required), Load, ListValue (func(ctx context.Context, value any) string) and Upload (optional).
pact.FormFieldValue What Decode and Load receive: the transaction, the record, the field, its options, the content locale (translatable types), whether the value lives in the host column, the value (always a string for a translatable type) and an exact reader of another attribute in the same locale.
pact.FormFieldDecoded Decode's answer: the Value to store (a string for a translatable type) and optional Notices.
pact.FormFieldNotice A message about a saved value: a phrase key or text with params; a positive Count selects the plural form.
pact.FormFieldUpload What Upload receives: the transaction, the scoped record (nil on the create form), the field, its options, the file name and the capped body.
pact.FormFieldUploadResult Upload's answer: the URL of the stored file.
pact.FormFieldError A value or upload a field type refuses: messages shown on the field (validation failed).
pact.ListRowStates Optional controller hook called once per list page; returns the states of the page's records, index-aligned.
pact.ListExtendRecords Optional controller hook called once per list page, before the columns are read; fills unstored fields of the page's records.
pact.FormRecordTitle Optional controller hook naming a record on its form and preview screens; sent as the record envelope's meta.title.
pact.RowState One state of a list row: pact.RowStateDeleted, pact.RowStateNegative or pact.RowStateDisabled.
pact.FormVirtualFields Optional controller list of form fields that are not model columns for the form (a password and its confirmation, for example): never bound, filled or returned; their submitted values reach the Form hooks through the admin framework's context accessor.
pact.FormRules Optional controller hook returning the validation rules of an admin save for create or update; the set replaces the model's Rules() for those saves and may name virtual fields.
pact.FilterScope Model scopes a list filter may call, limited to an exact allow list.
pact.FilterOptions Serves the choices of a scope filter. The admin controller may implement it and is asked first, so choices can be read from the database; otherwise the model is asked.
pact.RelationBeforeLink Optional controller hook that checks or fills pivot columns before a relation link is written.
pact.RelationBeforeCreate Optional controller hook run in the write transaction before a relation manager creates a related record.
pact.RelationAfterCreate Optional controller hook run after a relation manager creates a related record, before the commit.
pact.RelationBeforeUpdate Optional controller hook run in the write transaction before a relation manager updates a related record.
pact.RelationAfterUpdate Optional controller hook run after a relation manager updates a related record, before the commit.
pact.RelationBeforeDelete Optional controller hook run in the write transaction before a relation manager deletes a related record.
pact.RelationAfterDelete Optional controller hook run after a relation manager deletes a related record, before the commit.
pact.Option One dropdown choice (value and label), with Depth, the nesting level of a choice taken from a tree (omitted from JSON when 0).
pact.AdminSingleton Optional controller capability: SingletonRecord returns the one record the controller edits, creating it when absent; the admin opens it directly and the list, create, bulk and delete routes answer 404.
pact.DependentOptionsProvider Optional controller hook serving the choices of a dropdown with dependsOn from the submitted values of its siblings; asked by the options route and on every save.
pact.DependentOptionSet DependentOptions' answer: Applies (false when the sibling's value takes no choice; the field then stores null) and Options.
pact.HasMenuItemTypes Implemented by plugins that register menu item types; collected once at boot by signpost.
pact.MenuItemType One menu item type: Key, Label, DefaultPattern (required, starts with /), PatternFrom (a list type's record type), Placeholders, Nesting, DynamicItems, the optional References func and the required Resolve func.
pact.MenuItemReference One record a menu item can point at: value, label and nested records.
pact.MenuItemResolve What Resolve receives: type, reference, the layered URL pattern, whether to expand nested items and the unprefixed locale.
pact.ResolvedMenuItem A resolved item: title, URL (empty for a pure list type), modification time, ordered alternates and nested items.
pact.MenuItemAlternate The URL and title of a resolved item in one locale.

Dependencies

  • SummerCMS modules: bonfire (the command type in pact.HasCommands).
  • Third-party: github.com/go-gormigrate/gormigrate/v2, gorm.io/gorm.
  • Standard library: context, encoding/json, io, io/fs, net/http, regexp, strings, time.

Testing

go test ./modules/pact/...

The tests are compile-time interface checks and need no external services.