Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Design Notes

Design notes on the decisions, trade-offs, and implementation boundaries behind PIG.

Design notes explain the decisions and proposals behind PIG. Each note gives its date, status, scope, alternatives, and supporting evidence. Proposals describe directions under discussion; implemented and released records explain work that has reached those stages.

For command syntax and usage, use the PIG documentation. For changes in each version, use the release notes.

1 - From Human-Friendly to Agent-Native: PIG's CLI Contract

How PIG separates terminal text, structured results, execution plans, and environment context.

Decision date: 2026-02-12
Status: Released in pig v1.1.0 and refined by later command-layer work. The 2026-10-16 refinements below are Implemented in the v1.10.0 candidate, not yet released.
Current reference: pig command overview
Scope: PIG-owned commands and their machine-consumption contract; opaque passthrough commands keep their native interface.

Decision

PIG provides terminal text for people and structured results for automation. Commands that produce stable data expose JSON or YAML and status codes. Supported workflows also expose execution plans. Transparent passthroughs such as pig pt preserve the external tool’s output, prompts, and exit status.

Context

PIG began as a convenient package-management CLI. As it grew into PostgreSQL, Patroni, pgBackRest, Pigsty, and repository operations, a human-only interface created several problems:

  • automation had to scrape colored prose;
  • a zero process exit could hide a failed inner operation;
  • destructive workflows could not be inspected before execution;
  • an agent had to issue many discovery commands before understanding the host;
  • wrappers could accidentally mix subprocess chatter with structured output.

The v1.1.0 design introduced global output selection, stable result objects, execution plans, and the pig context snapshot. Later refactors narrowed these promises to commands that can actually own them reliably.

Alternatives considered

Three tempting approaches were rejected:

  1. Parse human text. It is fragile across wording, localization, colors, and upstream tools.
  2. Capture every subprocess into JSON. This breaks interactive programs, streaming output, terminal control, and native exit semantics.
  3. Invent one universal result schema. Package transactions, recovery plans, metrics, and context snapshots have different stable data; flattening them loses meaning.

Contract

The durable contract is:

  • text is the default interface for people;
  • a command advertises structured output only when PIG owns a stable result;
  • structured stdout contains one parseable result, while diagnostics and wrapped-tool output use stderr;
  • a plan describes intended actions, scope, risk, and expected effects without performing them;
  • destructive PIG-owned operations fail closed when confirmation is missing;
  • status codes distinguish usage, confirmation, environment, dependency, and execution failures;
  • pig context provides a bounded environment snapshot rather than forcing consumers to infer it;
  • transparent passthroughs retain the upstream contract.

The 2026-10-16 refinements apply the same contract to pig ext:

  • Availability checks and every summary format share matrix precedence, architecture aliases, and the metadata fallback. macOS reports declared PostgreSQL support without promising packages. This describes the catalog’s package snapshot. Default build targets retain their metadata and local-installation rules independently of the matrix.
  • Partial imports return failure while preserving resolved, downloaded, and unresolved items. Completed downloads remain on disk; the operation does not promise an atomic transaction.
  • pig pe treats HTTP 4xx/5xx as failure. Error details expose the numeric status and standard description, never the response body or server-supplied reason phrase.
  • pig license issue explicitly delivers a newly issued credential. Its successful structured result preserves the token in data.captured_output. Signing-key input is omitted from parameters, and raw key-loading errors are replaced with fixed messages at the command boundary. Parameter errors and safe signing-key diagnostics remain readable in every output format. Other sensitive command wrappers retain full output and error-detail redaction.

Consequences

This split makes scripts more reliable and lets agents choose commands based on risk and output capability. It also creates maintenance obligations: every structured field becomes compatibility surface, stdout purity needs tests, and a wrapper must not promise more stability than the tool it delegates to.

The output contract follows the command: PIG results for PIG operations, native output for forwarded tools.

Verification and evolution

Fixed source commits and regression tests for the committed v1.10.0 refinements cover: availability and counts, partial imports, exporter HTTP failures. These commits do not establish a release boundary.

The initial framework shipped with v1.1.0. The command-layer consolidation in fb93602 later removed duplicated wrappers and centralized plan and output glue. Patroni subsequently became a transparent passthrough, an example of reducing PIG-owned structure when the upstream interface is already authoritative.

Tests now cover structured stdout isolation, result rendering, plan behavior, confirmation gates, and context collection in the packages that own those contracts.

Current status

Use the output mode supported by each command. See the pig reference for structured results and passthrough behavior.

2 - Compile, Validate, Then Commit: The Native sty conf Pipeline

Why pig sty conf treats Inventory generation as a bounded compiler pipeline with path safety, structural mutations, secret discipline, and atomic output.

Decision date: 2026-02-18; the production contract was finalized on 2026-08-14.
Status: Implemented and released in pig v1.8.0.
Current reference: pig sty conf
Scope: Generating one validated static Inventory from a trusted Pigsty template; not arbitrary YAML transformation.

Decision

pig sty conf should behave like a small compiler: resolve one safe template, parse it, apply a bounded set of structural mutations, validate the complete candidate, and atomically commit the output only after every required stage succeeds.

The command does not invoke the legacy configure script and does not fall back to raw shell execution. Its structured result reports selected inputs, effective choices, applied change kinds, and warnings without returning generated secret values.

Context

Template configuration looks simple until paths, symlinks, multiple IP placeholders, version-pinned templates, mirrors, proxy environments, generated credentials, and partially valid YAML interact. A text replacement pipeline can cascade IP substitutions, rewrite unrelated domains, leak secrets, or truncate the destination after a late validation failure.

The output Inventory may contain administrative credentials, so both file handling and result rendering are part of the security boundary.

Alternatives considered

  • Call the existing shell configure script. Rejected because parsing, validation, and result semantics would remain outside PIG’s control.
  • Use global search and replace. Rejected because IP and domain values need exact placeholder boundaries and simultaneous mapping.
  • Write first and validate afterward. Rejected because a failed candidate could replace a usable Inventory.
  • Accept arbitrary absolute templates. Rejected because the command should compile known Pigsty modes, not become a privileged file copier.
  • Return generated passwords for convenience. Rejected because structured logs and agent traces are not secret-delivery channels.

Contract

  • templates resolve below the Pigsty configuration tree through safe relative names;
  • absolute paths, traversal, path escape, and direct, symlink, symlinked-parent, or hard-link source/output aliasing are rejected;
  • parsing and IP-collision checks precede external preflight;
  • placeholder IPs are mapped simultaneously and unrelated addresses remain unchanged;
  • domain replacement matches the exact template token;
  • profile, region, proxy, locale, and PostgreSQL-version changes are structural and bounded;
  • generated credentials use one random value per known identifier and expose only identifiers in results;
  • the complete candidate receives native validation and optional bounded Ansible parsing;
  • any failure leaves the destination untouched;
  • success writes atomically with mode 0600.

Consequences

Use sty conf to generate a new Inventory from a known template. Use the lossless pig inventory workflow to edit an existing Inventory.

Version-pinned templates keep their version and warn when a conflicting version request cannot apply.

Verification and evolution

The native configure direction was first recorded on 2026-02-18. The production refinement landed with 74e084e, and the final contract synchronization followed in adc4260. Tests cover traversal and aliasing, simultaneous IP mapping, domain boundaries, interactive and closed-input selection, version handling, proxy and region changes, secret generation and redaction, preflight ordering, validation failures, permissions, and atomic writes.

Current status

Use pig sty conf to generate a new Inventory from a Pigsty template and pig inventory to inspect or edit an existing declaration. Current flags, modes, and preflight behavior live in the pig sty reference.

3 - The 70% Tuner: Defining the Boundary of pig pg tune

How pig pg tune calculates initial PostgreSQL parameters from hardware and workload inputs.

Decision date: 2026-03-21
Status: Released in pig v1.3.2, following implementation on 2026-03-23.
Current reference: pig pg tune
Scope: Generate a starting configuration from hardware, PostgreSQL version, and workload profile.

Decision

pig pg tune calculates core PostgreSQL parameters and prints them for review. It detects CPU, memory, and disk capacity, accepts explicit overrides, and applies a selected workload profile. Text output displays the generated parameters; JSON and YAML output include the inputs and parameters. Review the values and select the settings to apply through your configuration system.

The original “70% tuner” name describes a practical starting point. It is not a measured accuracy score.

Context

A fresh installation needs an initial configuration before workload measurements are available. One static configuration cannot fit both a small development machine and a large database server. Hardware-based formulas provide a repeatable baseline that the operator can inspect and adjust.

Alternatives considered

  • Ship one fixed configuration. Memory and parallelism settings need to scale with the host.
  • Build an adaptive tuner. This would require workload data, experiments, and a feedback loop.
  • Apply settings automatically. The operator needs to review the generated values and choose how to deploy them through the existing configuration workflow.
  • Calculate every PostgreSQL parameter. Replication, security, and availability settings also depend on requirements that hardware detection cannot determine.

Contract

  • Use the detected hardware or the operator’s explicit CPU, memory, and disk values.
  • Select formulas through oltp, olap, tiny, or crit.
  • Produce the same parameters for the same resolved inputs and PostgreSQL version.
  • Include the resolved profile, hardware, and PostgreSQL version in structured results.
  • Print text parameters in a stable order and identify the SSD assumption in the header.
  • Validate input ranges and omit parameters unavailable in the selected PostgreSQL version.
  • Leave configuration files and the running server unchanged.

Consequences

Operators can use the output for a new installation or initial sizing. Review the values, apply them through the configuration system in use, then measure the workload. Query concurrency, checkpoint behavior, storage latency, and replication requirements guide later adjustments.

Verification and evolution

The initial implementation is 60eecfe. The v1.3.2 release introduced parameter generation. The current implementation returns a result for rendering. Tests in tune_test.go cover workload formulas, resource limits, overrides, and version-dependent parameters.

Current status

Parameter generation is released. See the pig pg reference for inputs and output examples.

4 - Why PIG Keeps the Cobra Command Layer Flat

The source-layout decision that keeps one top-level command in one cmd file and moves concrete behavior into cli and internal packages.

Decision date: 2026-06-30
Status: Implemented; the repository uses this layout for new commands.
Current reference: pig command overview and the source repository
Scope: Go source ownership and command registration, not the public command taxonomy itself.

Decision

The cmd package stays flat. One top-level command belongs in one top-level Go file: pg.go, pb.go, pt.go, pe.go, sty.go, do.go, repo.go, and their peers. Even large command trees remain in that entry-point file unless there is an explicit decision to change the layout.

The file may be long, but it should contain Cobra concerns: names, aliases, annotations, flags, argument validation, help, registration, and option mapping. Concrete work belongs in cli/*, internal/*, or another implementation package.

Context

Earlier command growth produced many small files named after subcommands and several parallel implementations of confirmation, structured output, plan rendering, and legacy wrapping. It became difficult to answer simple questions: where is a top-level command registered, which file owns an alias, and whether two helpers implement the same policy.

PIG’s command families are large, but their public grammar is one surface. Keeping that grammar in one place makes review and collision detection easier, while implementation packages remain decomposed by responsibility.

Alternatives considered

  • A directory per command under cmd. Rejected for normal commands because it scatters one public grammar across many packages and encourages business logic near Cobra.
  • One file per subcommand. Rejected because registration, aliases, and inherited flags become hard to audit as one contract.
  • Put everything in cmd. Rejected because tests, reuse, and error handling suffer when operational logic depends on Cobra state.
  • Abstract every repeated line. Rejected because speculative frameworks can hide the command grammar; only stable, cross-command glue should be shared.

Contract

  • cmd/root.go owns root setup, global flags, and top-level registration;
  • cmd/utils.go owns shared command-layer helpers;
  • each normal top-level command has one matching top-level source file; matching and focused test files may accompany it;
  • Cobra code validates syntax and maps options, but does not perform the operation;
  • reusable confirmation, annotation, structured-output, and plan helpers have one implementation;
  • implementation packages accept ordinary options and return typed results or errors without depending on Cobra globals.

Consequences

Some command files are intentionally large. The trade-off is accepted because the public surface can be reviewed as a unit, while the implementation remains split below it. The rule also reduces file churn when aliases or flags move and gives agents a deterministic starting point.

Review the separation of responsibilities, not file length. Operational logic belongs in the implementation packages, including logic inside closures.

Verification and evolution

The convention was recorded in 9eb70db, followed by the large command-surface consolidation in fb93602. Guard tests check alias collisions and command registration, while package tests exercise the implementation beneath the Cobra layer.

Current status

This remains the repository rule for new work. Public command documentation belongs on this site; source-layout enforcement remains close to the code so contributors and coding agents encounter it before editing.

5 - Why PIG Builds Debug Packages by Default

The build-policy decision that makes RPM debuginfo/debugsource and DEB dbgsym output opt-out rather than opt-in.

Decision date: 2026-09-03
Status: Released in v1.8.1.
Current reference: pig build
Scope: Debug-package policy for pig build ext and pig build pkg; individual package recipes remain authoritative for their payload.

Decision

pig build ext and pig build pkg request the platform’s normal debug-package output by default. PIG does not pass a suppression setting during an ordinary build. Users who need the smaller or faster artifact set opt out explicitly with --nodbg.

On RPM systems, the ordinary build leaves the distribution’s debuginfo and debugsource machinery enabled; --nodbg defines debug_package as empty. On Debian and Ubuntu, --nodbg adds noautodbgsym to DEB_BUILD_OPTIONS while preserving existing options. The old -s|--symbol spelling remains accepted as a deprecated compatibility no-op because debug output no longer needs to be enabled.

Context

The earlier command made RPM debug output opt-in with --symbol and otherwise injected debug_package %{nil}. That inverted the platform convention and made the default build less useful for post-crash analysis. Debian’s Debhelper already creates automatic dbgsym packages by default when eligible, and provides noautodbgsym as the explicit opt-out.

Debug packages increase build time, storage, and repository volume, but those costs are visible and manageable. Missing symbols are much harder to reconstruct after the exact source, compiler, and binary have moved on.

Alternatives considered

  • Keep --symbol as an opt-in. Rejected because release-quality artifacts should retain the matching diagnostic material by default.
  • Always require debug packages. Rejected because quick local builds and constrained artifact pipelines need an explicit opt-out.
  • Rewrite package recipes from PIG. Rejected because spec files and debian/rules own package payload details; mutating them at execution time would be surprising and difficult to audit.
  • Use nostrip for DEB opt-out. Rejected because it changes the main binary instead of merely suppressing the separate automatic debug package.

Contract

  • without --nodbg, PIG adds no debug-suppression macro or environment option;
  • on RPM, --nodbg adds --define "debug_package %{nil}" to rpmbuild;
  • on DEB, --nodbg appends noautodbgsym to DEB_BUILD_OPTIONS without discarding existing values such as parallel=N or nocheck;
  • --symbol remains parseable for compatibility but is hidden, deprecated, and redundant;
  • structured command results expose nodbg and retain symbol as the effective inverse value;
  • the normal installation package remains the build-success authority; debug packages are additional artifacts, not substitutes for it;
  • pure SQL, noarch, or otherwise ineligible builds need not emit an empty debug package;
  • an explicit decision inside a package recipe or an operator-supplied build environment remains authoritative. The command default is a policy request, not permission to rewrite recipes.

Consequences

Compiled extensions normally produce more files and consume more build and repository space. In return, a default build is suitable for stack traces, core analysis, and symbol servers without a second reconstruction build. Existing scripts that pass --symbol keep working, while scripts that intentionally want the old reduced artifact set must move to --nodbg.

Recipe repositories should remove blanket debug suppression where it does not represent a specific package constraint. That is a separate reviewed change because some packages are architecture independent, fail debug extraction, or use custom build systems.

Verification and evolution

The superseded opt-in behavior is pinned in the earlier build flag registration and rpmbuild command construction. The replacement was implemented in 1c6f524. Command tests keep --nodbg visible and the compatibility flag deprecated. Builder tests verify that the default RPM argv omits the suppression macro, the opt-out includes it, and the DEB environment preserves existing options while adding noautodbgsym once.

Platform validation should sample a compiled extension on one EL builder and one DEB builder, checking both default debug artifacts and their absence under --nodbg. Recipe-level exceptions must be reported separately rather than treated as proof that the PIG command ignored its option.

Current status

Released in v1.8.1. See the current reference linked above for usage and platform requirements.

6 - One Grammar for Dangerous Work: PIG's Operations CLI Safety Contract

How PIG separates primitives from orchestrators, makes destructive intent explicit, and prevents aliases or structured output from changing operational meaning.

Decision date: 2026-07-02
Status: Released for pg, pb, pt, and pitr by v1.5.0; the 2026-08-29 do and build proxy refinements were released in v1.8.1. The acceptance refinements below are Implemented for v1.10.0; source evidence is listed under verification. They are not yet released.
Current reference: pig pg, pig pb, pig pitr, pig do, and pig build
Scope: PIG-owned operational commands; transparent upstream commands retain upstream confirmation and exit behavior.

Decision

Operational convenience must not blur operational meaning. PIG distinguishes low-level primitives from multi-stage orchestrators, makes destructive intent explicit, reserves aliases carefully, and requires plans and structured results to describe the same action that text mode will execute.

The most important example is recovery: pig pb restore is the pgBackRest primitive, while pig pitr coordinates Patroni, PostgreSQL shutdown, restore, restart, and post-recovery guidance. An alias must never make those two paths look interchangeable.

Context

The first generation of convenience aliases accumulated inconsistent positional arguments, confirmation flags, output handling, and service semantics. Similar words such as restart, restore, promote, and failover can refer to very different layers. A short alias that crosses those layers can turn a harmless-looking invocation into an unmanaged destructive primitive.

Automation also exposed false-success risks when wrapper output, subprocess output, and result rendering used different definitions of success.

Alternatives considered

  • Maximize shorthand aliases. Rejected because collisions and cross-layer synonyms are more dangerous than a few saved characters are valuable.
  • Put confirmation on every risky-looking word. Rejected for passthrough commands because the upstream tool must own its prompt and semantics.
  • Make the orchestrator call a convenience alias of the primitive. Rejected because recovery coordination has additional stop, verification, and restart invariants.
  • Return success after launching the inner command. Rejected because the result must reflect the complete owned workflow.
  • Restrict maintenance database names to ASCII identifiers. Rejected because spaces and non-ASCII names are valid inputs. Reject libpq connection-string syntax instead, and preserve the positional boundary when generating commands.

Contract

  • sibling command names and aliases are unique;
  • an alias cannot shadow a different top-level command;
  • destructive PIG-owned operations require explicit confirmation and support non-mutating plans where a meaningful plan exists;
  • command-layer validation rejects malformed or extra positional arguments before side effects;
  • command-specific names mirror the downstream Pigsty contract, or use the narrowest documented safe boundary when the downstream playbook has no explicit grammar;
  • structured output and text mode share one result and one success definition;
  • credential-bearing values stay out of diagnostics, and readiness or connectivity failure remains a command failure rather than a logged warning followed by success;
  • low-level restore does not claim to manage Patroni or HA routing;
  • the PITR orchestrator stops the manager when required, proves PostgreSQL is stopped, restores, optionally starts PostgreSQL, and deliberately leaves Patroni stopped for operator verification;
  • native tools receive extra arguments only through an explicit, documented boundary.

The 2026-10-16 to 2026-10-17 refinements apply the same boundary to the v1.10.0 candidate:

  • Cross-user DBSU writes stage an exclusively created private sibling, use mode 0600 and the executing DBSU’s ownership, reject symlink/non-regular destinations, replace the exact path, and clean staging on failure. Linux uses an exact-target rename; macOS uses the system Perl rename syscall because BSD mv has directory-target semantics.
  • repo rm validates the whole operand list before changes. Only zero operands mean full backup; all remains node, pgsql, and infra. requested_modules retains the original request; removed_repos describes the expanded actions. repo add, repo set, and repo info also reject explicit empty names and comma-separated empty entries as a whole request. Only omitted operands retain the add/set default of all. Add/set also validate module-name syntax before initializing the repository manager. Unsafe names reject the whole request; syntactically valid unknown names retain additive partial success, while replacement rejects them before changes.
  • With an explicit pg -D, SQL operations and database enumeration bind to that directory’s recorded port and socket. Ambiguous metadata fails before execution. Omitting -D preserves native client connection defaults; /pg/data remains the default for instance operations. Explicit empty or whitespace-only -D values fail before configuration or operational work. Plans, confirmation retries, and verification commands preserve the same target and operation options, including an explicit -D /pg/data. VACUUM FULL plans require confirmation, as execution does. The 2026-10-17 maintenance refinement rejects connection-string database operands before plans, confirmation, or execution, without echoing the supplied value. A libpq connection string could otherwise override the selected host/port and expose credentials. vacuum, analyze, and freeze validate the complete enumerated database list before any maintenance starts. Generated vacuum and repack commands place all options before -- and the database name, preserving a name such as --all as a single-database operand.
  • Fork replacement requires matching metadata with an explicit target for nonempty unmanaged directories, including non-PGDATA directories. Empty destinations remain usable; managed forks must be replaced by name. Private paths are resolved as DBSU, and unresolved aliases fail closed. The 2026-10-17 review removes the extra /pg/data prohibition: eligibility depends on target ownership, source/destination relationships, and runtime state. A conventional path cannot identify every primary and may be a legitimate fork target. Forced cleanup of managed orphan directories remains supported. For unmanaged removal, a permission-denied local path check retries as the selected DBSU; it must establish that the target is a directory and not a symlink before deletion can proceed.
  • EPERM alone must not authorize pg init --force or PITR restore. Independent executable identity may establish PID reuse; unavailable identity remains unknown. A live PID must identify PostgreSQL before lifecycle commands may signal it or connection metadata may be used for SQL. Unknown state carries no usable PID for force-stop escalation. Fork start must not persist success in that state; list output reports unknown and runtime_unknown: true. Role detection also fails on an unknown running state; file-based role evidence cannot override a confirmed stopped state. Forced initialization reports probe failures separately from a running conflict. Plan preconditions report indeterminate runtime state as unknown, never a passed check with PID zero. Status queries distinguish permission failures (130201, exit 3) from other probe failures (130808, exit 1); partial data in either failure cannot establish that the instance is stopped.
  • Timeline reporting binds SQL to the selected data directory’s valid PID metadata and socket. If that cannot be resolved, it uses that directory’s pg_controldata or reports an unknown timeline. Promotion execution and the requirement to verify the primary role stay unchanged.
  • PITR cancellation and EOF return confirmation-required (161001, exit 7) before service stops, restore, or restart. Acceptance must execute the confirmation path and observe zero lifecycle side effects.
  • PIG_NON_INTERACTIVE and structured execution disable prompt stdin and add sudo -n, including legacy structured wrappers. Native passthrough and explicit data stdin retain their contracts.
  • Sensitive captured output and error details remain private. Signing-key load errors never echo the supplied key. Explicit raw exports remain available on their documented text surfaces.

Consequences

Some historical shorthand disappeared and some scripts had to adopt cluster-first or explicit target syntax. In return, command names now preserve layer boundaries, plans correspond to real actions, and recovery automation cannot silently substitute a primitive for the orchestrator.

Plans show the affected resources and actions so operators can review recovery before execution.

Verification and evolution

Fixed source commits and regression tests for the committed v1.10.0 refinements cover: runtime identity and SQL targets, fork ownership and private paths, PITR cancellation and unknown state, plan target preservation, status error classification. These commits do not establish a release boundary. The later maintenance database-name and -- operand boundaries include Cobra argument-reparse tests and a real libpq target-boundary regression test. The repository operand preflight rejects unsafe module names before key preparation or repository writes and preserves partial additive results for syntactically valid unknown modules.

The normative command specifications entered the repository in c62c0f5. Subsequent commits aligned aliases, early validation, restore targets, service semantics, and role detection. Guard tests traverse the Cobra tree to reject sibling and cross-layer alias collisions. Recovery tests cover plan, confirmation, stop escalation, side restores, restart behavior, and structured failure results. Maintenance regression checks must reparse generated commands with Cobra and exercise the real libpq client against controlled local endpoints; checking for -h and -p in argv alone is insufficient.

Patroni later moved to transparent passthrough. That refinement keeps the same safety principle: PIG owns safeguards only for workflows it owns.

The same contract was applied to pig do name and cluster validation in 3e1603b, with Ansible built-in targets closed in a880485. Package-backed, credential-safe, truthful build proxy setup entered in 220ef9c, followed by structured-argument redaction and corrected machine annotations in 74cb128, and optional operands were reflected in the machine grammar in de7ffd0. The changes were exercised on Ubuntu 24.04 and Rocky Linux 9 ARM64 Farrow guests, then released from source commit 1c6f524 after the v1.8.1 CI run passed.

Current status

Use pig pb restore when you intentionally want the pgBackRest primitive and pig pitr when you want the managed recovery workflow. See the command reference pages for current syntax and platform requirements.

7 - Edit the Declaration, Preserve the Document: Lossless Pigsty Inventory

Why pig inventory separates YAML semantics from source bytes so scoped edits can preserve comments, ordering, anchors, and formatting.

Decision date: 2026-07-18
Status: Implemented and released in pig v1.6.0. The 2026-10-16 to 2026-10-17 refinements below are Implemented in the v1.10.0 candidate, not yet released.
Current reference: pig inventory
Scope: Static Pigsty Inventory inspection, scoped editing, validation, comparison, and safe writes.

Decision

PIG treats pigsty.yml as both a semantic declaration and a human-maintained source document. Semantic parsers determine what the Inventory means; the original bytes remain authoritative for how it is written. Scoped edits replace bounded source ranges and then reparse the complete candidate before an atomic write.

This avoids a common YAML-tool failure: a logically correct edit that silently rewrites comments, key order, quoting, anchors, block scalars, or line endings across the entire file.

Context

Pigsty Inventories are long-lived operational assets. They contain topology, tuning, credentials, comments, examples, anchors, and locally meaningful ordering. A conventional parse-mutate-serialize cycle can produce a valid but unreviewable diff and may change constructs the operator never selected.

At the same time, raw text editing without semantic validation can put invalid or contradictory configuration on disk. The design needed source fidelity and whole-document correctness together.

Alternatives considered

  • Round-trip through one YAML serializer. Rejected because no selected serializer preserved the full source contract byte-for-byte across the real Pigsty corpus.
  • Use regular expressions for YAML. Rejected because quoting, comments, aliases, block scalars, and nested collections make text-only semantic decisions unsafe.
  • Edit only a normalized generated copy. Rejected because the active Inventory is operator-owned and would still diverge from the generated representation.
  • Allow every node type to be replaced. Rejected because anchors, aliases, tags, and block scalars need stricter handling than ordinary mappings and scalars.

Contract

  • duplicate keys and multi-document YAML are rejected;
  • selectors address one unambiguous declaration fragment;
  • semantic decoding and source-range discovery are separate concerns;
  • an edit starts from the exact source revision and fails if the file changes concurrently;
  • the edited fragment is normalized only as required for its insertion context;
  • the complete candidate is reparsed and validated before commit;
  • writes use a same-directory temporary file, sync, rename, and directory sync;
  • symlink and unsafe path changes are rejected;
  • a successful edit removes group and other permissions, preserving the owner’s existing read and write permissions;
  • diagnostics, diffs, plans, and structured results omit declaration values unless a command is explicitly a raw text surface.

The 2026-10-16 acceptance refinement preserves role-name strings and structured role grants in pg_users[].roles. Editing, serialization, and CMDB export must preserve YAML 1.1 string semantics, including "1:30"; "010" is a regression control, not a claim of an earlier defect. The 2026-10-17 refinement also requires dates and timestamps supplied as strings to remain strings when loaded by PyYAML. Existing timestamp values retain their source bytes as opaque scalars; this does not add date arithmetic or normalize the document. Acceptance compares emitted types with an independent YAML loader, rather than relying only on PIG’s own round trip.

sty deploy and do consume the configuration layer’s already resolved Inventory path. Flag and environment relative paths use the caller’s cwd, quoted ~/ uses the user’s home, and defaults remain explicit at execution. Missing sources do not fall back. Static inspection still never executes dynamic sources; Ansible retains its execution boundary.

Consequences

The editor is more complex than ordinary YAML marshaling, and some syntactically valid fragments are deliberately refused when source fidelity cannot be proven. The benefit is a reviewable diff: unselected parts of the Inventory remain byte-for-byte stable, invalid YAML cannot be committed, and a concurrent edit cannot be overwritten silently.

Inventory can contain custom credential fields, so field names alone cannot reliably identify secrets. For this reason, show prints the selected declaration as raw text. Metadata and validation commands omit declaration values.

Verification and evolution

Fixed source commits and regression tests for the committed v1.10.0 refinements cover: resolved sources and role grants, date-like string preservation, independent YAML consumer acceptance. These commits do not establish a release boundary.

The root Inventory contract and existing-CMDB boundary were established in ba6e678, with the implementation landing in ea43858. Tests exercise real Pigsty configuration corpora, byte-identical round trips, scoped replacement, protected YAML forms, concurrent-change rejection, atomic-write failures, selectors, validation, and secret-free diagnostics.

Later refactors removed legacy options and separated validation stages without changing the source-fidelity model.

Current status

The static Inventory remains the primary declaration surface. Use the current pig inventory reference for selectors, validation profiles, structured output, and the explicitly experimental CMDB bridge.

8 - Use the CMDB Pigsty Already Has

Why PIG retired a new revision-store design and became a thin, guarded adapter to Pigsty’s existing PostgreSQL CMDB.

Decision date: 2026-07-18
Status: Released in v1.6.0 as an experimental adapter. The earlier revision-store design is superseded.
Current reference: pig inventory cmdb
Scope: Exchanging declarations with Pigsty’s existing CMDB, not designing another configuration database.

Decision

PIG must reuse the CMDB already provided by Pigsty. Its responsibility is a bounded adapter: validate a static Inventory, load declarations into the existing tables, dump the existing projection, check consistency, and switch Ansible between the static and dynamic sources safely.

PIG does not own a second schema, migration history, snapshot ledger, compare-and-swap revision store, three-way merge engine, or rollback database.

Context

An early design treated CMDB support as a greenfield backend. It proposed a separate schema, immutable snapshots, revision tokens, merge and rollback operations, backup bundles, and source switch records. The design was internally coherent but started from the wrong premise: Pigsty already had the pigsty and pglog schemas, load scripts, dynamic Inventory projection, and source switching behavior.

Building a parallel control plane would duplicate facts, create synchronization problems, and make PIG responsible for a data model owned by another project.

Alternatives considered

  • Keep the new revision store as an advanced mode. Rejected because two authorities are still two authorities, even if one is optional.
  • Mirror between the new and existing schemas. Rejected because conflict resolution and migration would become permanent product responsibilities.
  • Hide the existing scripts behind a shell wrapper. Rejected because PIG needs bounded timeouts, safe connection handling, structured plans, and atomic source switching.
  • Remove CMDB support entirely. Rejected because a small native adapter adds useful validation and automation without redefining the schema.

Contract

  • Pigsty’s existing schema and projections are the data-model authority;
  • PIG connects through an explicit database target, environment configuration, or service=meta;
  • credentials, DSNs, SQL bodies, and declaration values never enter plans or diagnostics;
  • check is read-only;
  • init replaces the pigsty and pglog schemas from the version-matched baseline and requires confirmation; back up an existing database first;
  • load replaces declaration rows transactionally and requires explicit confirmation;
  • dump refuses an unexpected overwrite unless forced;
  • enable and disable edit only recognized Ansible Inventory source forms and write atomically;
  • unfamiliar executable Inventory sources are refused rather than rewritten;
  • the entire command family remains labeled experimental.

Consequences

The correction removed the parallel revision-store implementation and its maintenance burden.

The remaining adapter is smaller, easier to audit, and compatible with existing Pigsty operations. It also inherits the limits of that system: init needs an operator-managed backup, and loading a declaration set is a replacement operation rather than collaborative version control.

Verification and evolution

The corrected boundary was recorded in ba6e678. The abandoned implementation was removed in e0f73ed, deleting the parallel schema, snapshot, merge, revision, and rollback machinery. Tests for the retained path cover PostgreSQL compatibility, connection redaction, transaction failures, digest-pinned confirmation, dump safety, and atomic source switching.

Current status

The existing-CMDB adapter shipped in v1.6.0 but remains experimental. Back up CMDB state before initialization or replacement. See the pig inventory reference for the supported workflow.

9 - A Bounded Grafana Client Instead of Dashboard Shell Scripts

How pig sty grafana manages dashboards and preferences through a native HTTP client.

Decision date: 2026-07-18
Status: Released in v1.6.0; dashboard schema v2 support followed in v1.6.2.
Current reference: pig sty grafana
Scope: Dashboard import, export, cleanup, and selected organization and user preferences.

Decision

PIG uses a native HTTP client to manage Grafana dashboards. Local JSON paths select a dashboard, a folder, or a complete dashboard directory. This gives load, dump, and cleanup a common selection model and lets users preview destructive work.

The default directory is the Pigsty dashboard bundle. Users can select another compatible directory. Cleanup normally selects dashboard UIDs declared by the local files; explicit clean --all selects all dashboards and folders in the active Grafana organization.

Context

The previous shell scripts depended on local file layout and offered limited detail about partial failures. Moving these operations into PIG makes timeouts, authentication, path validation, and results consistent. A local declaration also provides a precise default cleanup scope.

Alternatives considered

  • Keep shell scripts as the public interface. Network limits, redaction, and structured results would remain split across scripts.
  • Expose arbitrary Grafana API calls. PIG only needs dashboard operations and a few preferences.
  • Select cleanup targets by folder name. Names can collide. The selected JSON files provide dashboard UIDs; folders are removed only after they are empty.
  • Embed a default password. Credentials belong in user configuration.

Contract

  • Limit request duration and response size, and reject unsafe redirects.
  • Check public health before authentication. info can return health alone when no password is set.
  • Resolve credentials from explicit inputs, environment, or Inventory; omit them and response bodies from diagnostics and structured results.
  • list lists all dashboards in the active organization.
  • load validates and imports the selected local files without deleting live dashboards.
  • boot selects the existing pigsty dashboard as home, stars it, and names the organization Pigsty.
  • init loads a complete dashboard directory and runs boot only after every load succeeds.
  • dump exports the selected remote scope as deterministic JSON with atomic file writes.
  • clean deletes locally declared UIDs and then empty folders. --all explicitly selects the entire active organization. Both modes show a plan and require confirmation.
  • lang and style update the selected preference for the organization and current user. Style auto maps to Grafana’s system value.
  • Normalize supported dashboard schema v1 and v2 representations when reading and writing them.

Consequences

Load and cleanup use the same local declarations, so users can operate on one dashboard or folder. A matching UID identifies the target; it does not prove that PIG originally created it. Users must review the cleanup plan, especially when selecting --all.

Datasource provisioning, plugin installation, and user administration remain in Grafana’s own tools.

Verification and evolution

The native workflow landed in 3060485. Dashboard schema v2 support followed in 67f6e3b. The released tests cover health-only operation, authentication, load scopes, bootstrap ordering, export stability, preference updates, local UID cleanup, explicit full cleanup, and changed-plan rejection. HTTP-client tests cover timeouts, redirects, response limits, and redaction.

Current status

The dashboard workflow is released. See pig sty grafana for path selection, credentials, and cleanup examples.

10 - Let Patronictl Speak for Itself

Why pig pt stopped mirroring Patronictl’s command tree and became a transparent launcher with only a few PIG-owned local helpers.

Decision date: 2026-07-21
Status: Implemented and released in pig v1.6.0.
Current reference: pig pt
Scope: Patronictl-backed cluster commands plus PIG-owned configuration selection, settings sugar, service, status, and log helpers.

Decision

pig pt is a transparent launcher for the installed patronictl. PIG selects the configuration and dispatches a small set of local helpers. Every other command token and all following arguments are passed unchanged, with native prompts, terminal behavior, output formats, and exit codes.

PIG no longer maintains a copy of Patronictl’s evolving command tree.

Context

Mirroring Patronictl required PIG to reproduce commands, options, positional grammar, confirmation, formatting, and version-dependent behavior. That surface changed upstream and PIG’s copy drifted. Users could receive different semantics depending on whether they called patronictl directly or through PIG.

The wrapper still adds value in a Pigsty environment: selecting the correct config as the database operating-system user, providing local service and log workflows, and translating a small settings operation into one native edit-config call.

Alternatives considered

  • Continue mirroring every upstream command. Rejected because it guarantees lag and duplicates validation that Patronictl already owns.
  • Allow only a tested command allowlist. Rejected because new upstream commands would remain unavailable until a PIG release.
  • Capture native output into PIG JSON. Rejected because it breaks interactive editing, streaming, prompts, terminal fidelity, and upstream schemas.
  • Remove pig pt entirely. Rejected because deterministic config selection and local Pigsty helpers remain useful.

Contract

  • the first non-option command token determines local dispatch or passthrough;
  • set, local service shortcuts, status, and log are PIG-owned;
  • all other commands and remaining tokens are forwarded verbatim;
  • pig pt -- COMMAND ... bypasses a local-name collision explicitly;
  • wrapper-level options must precede the native command token;
  • native help can run without resolving a local Patroni configuration;
  • Patronictl owns interactive prompts, native --format, and exit codes;
  • global PIG structured output is rejected where it would consume a native option ambiguously;
  • the selected config is resolved predictably and the process runs as the database system user.

Consequences

Automation had to adopt Patronictl’s cluster-first positional grammar and native output flags. Some PIG-only aliases and result schemas disappeared. In exchange, new Patronictl features work without a PIG release and behavior no longer depends on a lagging wrapper implementation.

The local set helper remains intentionally small: it classifies scalar Patroni keys and PostgreSQL parameters, then performs one native edit-config action.

Verification and evolution

The rewrite landed in 6cbc23b. Tests cover token-boundary parsing, verbatim argv preservation, config precedence, database-user execution, native exit propagation, help without configuration, output-mode rejection, the -- escape, and local-helper collisions. The final help-path refinement landed before v1.6.0.

Current status

Use Patronictl’s own documentation for forwarded command grammar and pig pt for PIG’s config selection and local helpers. For JSON output from a forwarded command, use Patronictl’s native --format json option.

11 - PIG 2.0 Product Direction

The proposed PIG 2.0 boundary: a stable Pigsty onboarding front door, verifiable Catalog client, and thin orchestrator that keeps deployment explicit.

Decision date: 2026-08-13
Status: Proposal for review; not implemented.
Current reference: PIG documentation and release history
Scope: Candidate product boundaries and verification gates for a future PIG 2.0 / Pigsty 5.0 line.

Decision

The proposed direction makes PIG the stable onboarding front door from an empty controller to a validated, deployable Pigsty Inventory. PIG would own Catalog selection, resolution, plans, bounded execution orchestration, structured results, and redacted receipts. It would continue to delegate package transactions, configuration application, and infrastructure state to DNF/APT, Ansible, and future provider-specific tools.

The proposal deliberately preserves PIG’s standalone value: repo, ext, and install must work without a Pigsty project. It also preserves explicit deployment consent: a future pig sty setup may download, bootstrap, and configure, but it must stop before multi-node deploy unless the user invokes deployment separately.

Context

By v1.8.0 PIG could download releases, bootstrap a controller natively, compile Inventory, manage repositories and extensions, and run selected operations. Several product seams remained:

  • repository, package-alias, extension, route, and Pigsty metadata could change independently;
  • projects had no explicit Catalog identity to protect later resolution from global updates;
  • route choice and repository safety were not one visible product contract;
  • execution results did not yet form a durable, redacted receipt across the onboarding path;
  • compatibility among PIG, Pigsty, Catalog schema, operating systems, and Ansible needed one release matrix rather than separate assumptions.

The proposal focuses 2.0 work on metadata consistency and a repeatable setup workflow.

Alternatives considered

  • Turn PIG into a monolithic configuration and state engine. Rejected because Inventory, Catalog authoring, package managers, Ansible, and providers already own different facts.
  • Make Pigsty depend on a live PIG or pgext checkout. Rejected because a Pigsty release must remain independently usable from generated, versioned artifacts.
  • Make setup deploy automatically. Rejected because creating and validating configuration is a different consent boundary from changing remote nodes.
  • Reimplement DNF/APT failover or Ansible execution. Rejected because PIG should select inputs and explain results, not become another package manager or configuration engine.
  • Block 2.0 on Vagrant/Terraform unification. Rejected because lab providers have different state semantics and do not determine the core onboarding path.
  • Invent a universal sty plan immediately. Rejected until at least two owned workflows prove that one reusable plan schema exists.

Contract

If accepted, the product direction would enforce these boundaries:

  • each fact type has one authority: Inventory for cluster declarations, Catalog authoring sources for product metadata, project lock for selected snapshot identity, receipts for observed results, and providers for live state;
  • PIG owns Catalog schema, validation, client, resolver, and selection, but not every authoring database;
  • sty setup composes existing init, boot, and configure use cases instead of duplicating them;
  • setup stops after a validated Inventory; deploy remains explicit;
  • standalone commands follow a compatible Catalog channel, while a Pigsty project pins its selected snapshot after a successful setup or configuration commit;
  • ordinary commands never rewrite an existing project lock implicitly;
  • route selection is explicit or a bounded first-run decision, not a continuous GeoIP, cloud-IMDS, or background-latency service;
  • package download retry and endpoint failover remain owned by DNF/APT;
  • execution artifacts are versioned and redacted; raw upstream modes preserve native streams and exit behavior;
  • doctor remains diagnostic and does not gain default repair authority;
  • future lab support is a thin adapter and never makes PIG the owner of Terraform state.

Consequences

The proposal creates a clearer first-run story and makes metadata selection auditable. It also adds new durable contracts: snapshot identities, project locks, migration rules, trust policy, receipts, and compatibility matrices. Those contracts increase the testing and release burden and must not ship as loosely coupled features.

An Ansible event bridge depends on redaction and compatibility tests. Doctor/support bundles and lab adapters are deferred until after the initial release. The EL7 support level remains open.

Verification and evolution

This proposal requires evidence before it can become a release contract:

  • a native onboarding VM matrix across the declared Linux targets;
  • adversarial Catalog signature, expiry, rollback, mix-and-match, and offline tests;
  • semantic-diff proof that generated Pig, Pigsty, and pgext consumers do not drift;
  • an Ansible callback experiment with zero no_log or secret leakage;
  • route-selection tests for global, China, proxy, and restricted-network environments;
  • repository-signing tests before secure defaults are changed;
  • rehearsed 1.x-to-2.0 layout, lock, and mixed-version migration;
  • schema and structured-output fixtures tied to an explicit compatibility matrix.

The implementation baseline used for this proposal is v1.8.0. That release contains native boot and configure, but it does not implement the proposed Catalog v2, project pin, setup command, event receipt, or 2.0 migration contract.

Current status

The proposal remains open. Decisions are still needed for Catalog security, overlays, paths, EL7 support, and the Ansible event bridge. Use the current documentation for released commands.

12 - Catalog v2 Proposal: Immutable Typed Snapshots

A proposed verifiable Catalog model with typed targets, content-addressed snapshots, explicit activation, project pins, and offline import.

Decision date: 2026-08-13
Status: Proposal; security mechanism, overlays, packaging, and path layout remain open.
Current reference: pig ext and pig repo describe the v1 Catalog behavior.
Scope: A candidate PIG 2.0 publication and consumption model for product metadata, not Inventory or live system state.

Decision

Catalog v2 should be one immutable, verifiable snapshot composed of multiple typed targets. A manifest binds platform, repository, route, package-alias, extension, compatibility, Pigsty release, and public-key metadata to exact bytes. Candidate targets are validated together and activated through one pointer, preventing a new repository catalog from being mixed silently with an old extension matrix.

The snapshot digest is its identity. Package, system, user, portable, and project scopes store or select the same verified content; they do not merge unrelated base snapshots into a synthetic Catalog.

Context

The v1 Catalog is practical but spreads related facts across embedded CSV, repository YAML, Pigsty variables, generated sites, and reload paths. Some fields repeat derived information, and the extension matrix compresses several independent identities into one record. Independent updates make it difficult to prove that repository, package, extension, and compatibility data belong to the same publication event.

Catalog v2 must define how related data is published, verified, and activated together.

Alternatives considered

  • Create a larger extension.csv or one giant YAML file. Rejected because unrelated target types evolve differently and cannot be activated or streamed independently.
  • Use SQLite, protobuf, or a custom binary matrix immediately. Rejected because the current dataset has not demonstrated a performance need that justifies a new runtime and debugging cost.
  • Merge system, user, and project base snapshots field by field. Rejected because the result has no single publisher, digest, compatibility statement, or signature.
  • Store active or project-pinned content only in a cache. Rejected because deleting a cache must not destroy a durable user or project decision.
  • Let a user file shadow the official trust root. Rejected because security policy is a constraint, not an ordinary last-writer-wins preference.
  • Silently update and activate in the background. Rejected because metadata changes can alter package resolution and must be an observable operation.

Contract

The proposed snapshot contract is:

  • deterministic UTF-8 JSON for manifests and small targets, and JSONL for large sparse targets;
  • raw manifest bytes and target length/hash are verified without parse-and-reserialize ambiguity;
  • manifests carry schema, monotonic security version, creation, expiry, channel, and PIG/Pigsty compatibility;
  • an embedded rescue baseline is always available;
  • package-owned baselines, durable snapshot stores, mutable state pointers, and purgeable download caches are separate paths;
  • Linux follows FHS/XDG, macOS uses Application Support and Caches, and portable PIG_HOME keeps config, data, state, cache, and run roles distinct;
  • a project lock records snapshot identity and ordered overlays, while the verified snapshot is materialized into durable project support data;
  • selecting a digest and finding its bytes are separate algorithms;
  • system security policy can only be tightened by lower scopes, not weakened silently;
  • updates download into private staging, verify every layer, sync, move into a content-addressed store, and atomically replace the active pointer;
  • failures retain the previous active snapshot;
  • project pins do not move when user or system active channels update;
  • offline export includes verification metadata and public trust material, never private keys;
  • old ext reload and repo reload may map to one whole-snapshot update for one compatibility period, but cannot activate targets independently.

The runtime Catalog excludes Inventory, credentials, live probes, installed-package state, Ansible events, provider state, confirmations, and metrics that change without a product decision.

Consequences

Typed targets make ownership and validation clearer, and content addressing makes rollback and airgap import auditable. Project materialization prevents a deployment from depending on one user’s global cache. Independent OS packages can refresh a read-only baseline without changing an active user selection.

The cost is a larger release protocol: key rotation, expiry, rollback protection, garbage collection, path permissions, migration, package-manager lifecycle, overlay conflicts, and cross-repository generators all become compatibility-sensitive work.

The extension model must also separate SQL extension identity, upstream project, distribution or build unit, versioned release, OS package offer, target availability, and display policy. Derived fields such as aggregate platform support or required_by should be generated, not maintained as a second truth.

Verification and evolution

Before implementation, the proposal requires an ADR to choose between go-tuf and a minimal signed manifest. Both candidates must pass the same threat tests: bad signatures, expired metadata, rollback, target substitution, mix-and-match snapshots, truncated downloads, and offline verification. If neither passes, Catalog v2 cannot be a 2.0 release feature.

Additional gates cover FHS/XDG/macOS/portable paths, root and non-root permissions, read-only homes and projects, symlink replacement, disk-full and concurrent activation, package upgrade/remove semantics, project survival after global-cache deletion, and parity migration of the current extension and availability corpus.

The proposal was based on v1.8.0. v1.9.0 still uses the embedded and independently reloadable v1 Catalog.

Current status

Catalog v2 is not implemented. Security, paths, and overlay rules need decisions and tests first. If overlay conflict and trust rules are not ready, a complete custom signed channel can provide the initial customization path. Current usage is documented under pig ext and pig repo.

13 - One-command Xray Clients and Servers

One-command VLESS, REALITY, and Vision client/server setup, stable server credentials, and verified proxy connectivity.

Decision date: 2026-10-05
Status: Released in v1.9.0. The 2026-10-16 refinements below are Implemented in the v1.10.0 candidate, not yet released.
Current reference: pig build, operations safety, and release history
Scope: One-command Xray client and server setup; Linux clients and servers, plus a macOS client. Shared Nginx ingress remains a separate deployment boundary.

Decision

Extend pig build proxy with explicit client and server subcommands. Use VLESS over RAW, REALITY, and xtls-rprx-vision for the new workflow. First server setup generates matching credentials; repeated setup preserves the existing client connection contract and offers the same standard URI. The client takes a URI or explicit command-line parameters, installs Xray, configures the local listener, starts its service, and verifies the connection. A URI can be pasted directly or read from a file or stdin.

Retain the existing command path and x alias. The zero-argument command installs or verifies Xray. The historical id@host:port [local] form retains its VMess meaning and default port during a compatibility period, through the existing V2Ray workflow.

Context

The previous implementation installs vray, writes an HTTP-to-VMess configuration to /etc/v2ray.json, grants the v2ray group access, writes shell aliases, restarts v2ray, and checks an HTTPS request. Existing tests cover argument parsing, package-manager selection, and credential redaction.

The inspected reference pair uses Xray 26.3.27. The running macOS client exposes HTTP and SOCKS on one loopback port, 8888, and uses VLESS/RAW/REALITY/Vision with a Chrome fingerprint, disabled multiplexing, and ML-DSA-65 verification. Both HTTP and SOCKS requests succeeded during the review. The client UUID, short ID, and SNI match the server’s corresponding fields.

The reference Linux server listens on loopback port 9443. Nginx owns public port 443 and selects the backend by SNI, forwarding a PROXY protocol header. Xray accepts that header, connects to a REALITY target, sends allowed traffic through freedom, and blocks destinations matching geoip:private. Nginx provides the public entry point in this deployment.

Inspected Pigsty RPM and DEB artifacts for both architectures provide /usr/bin/xray, /etc/xray.json with mode 0640 and ownership root:xray, an xray:xray service account, xray.service, and geodata in /usr/share/xray. Their installation scripts reload systemd without automatically starting or enabling the service. Public repository indexes list version 26.3.27-1PGSTY. Runtime tests are listed below.

Alternatives considered

  • Rename V2Ray paths and keep only VMess. Insufficient: it does not provide server setup or the working reference protocol. The old template passes Xray’s configuration validator, but Xray emits a VMess deprecation warning.
  • Build a general proxy manager. Deferred: subscriptions, protocol catalogs, TUN, transparent routing, traffic accounting, and arbitrary configuration import are outside the requested pair.
  • Create a new top-level command now. Deferred: the existing build proxy entry can hold the two roles without changing the root grammar or creating a second implementation.
  • Copy the reference server verbatim. Rejected: a loopback listener requiring PROXY protocol cannot serve direct public connections, and existing Nginx port ownership must be respected.
  • Require a connection file for client setup. Rejected: pasting one URI or supplying parameters directly is the primary workflow; a protected file or stdin remains useful for automation.
  • Print generated credentials in ordinary success output. Rejected: structured results and routine logs must remain safe to retain. An explicit export can return a URI to the terminal.

Contract

The implemented role grammar is:

# On a Linux server, explicitly print the connection URI for copying.
pig build proxy server --host proxy.example.com --target www.example.net:443 --export -

# On a Linux or macOS client, paste the URI returned by the server.
pig build proxy client 'vless://[email protected]:443?encryption=none&security=reality&type=tcp&flow=xtls-rprx-vision&sni=www.example.net&fp=chrome&pbk=PUBLIC_KEY&sid=SHORT_ID&pqv=VERIFY_KEY'

# Alternatively supply connection parameters directly.
pig build proxy client --server proxy.example.com:443 --id UUID --sni www.example.net --public-key PUBLIC_KEY --short-id SHORT_ID --pqv VERIFY_KEY

# Optional protected file input.
pig build proxy client --from ./client.uri

# Reuse a running Linux server's credentials without changing its deployment.
pig build proxy server --host proxy.example.com --export-only --export -

# Non-mutating preview using the default client listener, 127.0.0.1:12345.
pig build proxy client --from ./client.uri --plan

# Explicitly match the inspected client's listener.
pig build proxy client --from ./client.uri --listen 127.0.0.1:8888

# Xray backend behind an already-configured trusted ingress; default bind is 127.0.0.1:9443.
pig build proxy server --host proxy.example.com --target www.example.net:443 --proxy-protocol --export ./client.uri

Server inputs and defaults. Require the advertised --host and a REALITY --target. Public --port defaults to 443; the direct listener defaults to that port on all IPv4 interfaces. On first setup with --proxy-protocol, the listener instead defaults to 127.0.0.1:9443. An explicit --listen changes the bind endpoint without changing the advertised public port. On repeated setup, omitting --listen preserves the existing supported listener rather than replacing it with a first-setup default. On first setup, derive SNI from the target hostname unless --sni is supplied. On repeated setup, preserve the existing SNI instead of deriving a replacement. Validate the target’s TLS compatibility before applying configuration; do not silently choose an unrelated camouflage host.

Generate a UUID, X25519 key pair, random eight-byte short ID, and a distinct ML-DSA-65 seed and verification key. Reuse existing PIG-managed material on repeated setup; ordinary reruns do not rotate credentials. Rotation and multi-user management require a later explicit contract. Use warning logging, disabled access logging, decryption: none, the Vision flow, and an explicit private-destination blocking rule. Set the geodata environment to the packaged asset path.

Server idempotence. Repeating setup with the same inputs must leave existing clients usable without changing their configuration. Preserve the UUID, X25519 private key and corresponding client authentication material, short ID, ML-DSA seed and verification key, SNI, and protocol defaults. With the same advertised host and public port, export the same canonical URI. Generate credentials only when creating a server configuration for the first time, never because setup runs again.

If the managed configuration and service settings already match the requested state and the service is running and enabled, perform checks without rewriting files or restarting the service. If only the service has stopped or become disabled, restore the requested service state using the existing credentials. Missing, malformed, or ambiguous existing authentication material is an error; do not repair it by silently regenerating credentials. Reject setup inputs that conflict with the existing authentication or SNI contract. Such changes need an explicit migration or rotation contract rather than an ordinary rerun.

Reuse an existing server. server --export-only --host HOST --export DESTINATION reads the existing packaged server configuration and exports a connection URI using its current UUID, SNI, short ID, and derived client verification material. The advertised public port still defaults to 443; never infer it from a loopback backend port. Require one unambiguous supported VLESS/REALITY inbound, client, SNI, and short ID. Reject ambiguity or malformed material instead of selecting an arbitrary entry. This mode does not install, rewrite server or service configuration, restart, enable, or rotate credentials. An explicitly requested file export is its only persistent write. Derive verification material in process so existing private keys and seeds do not enter subprocess arguments. This branch performs a read-only query with explicit credential export; the shared server command retains its conservative action annotation.

Client inputs and defaults. Accept a single quoted vless:// positional URI as the primary workflow. Also support direct parameter mode with --server host:port, --id, --sni, --public-key, and --short-id; --pqv supplies ML-DSA verification when used by the server. The direct mode supplies the connection fields while REALITY, RAW, Vision, and Chrome retain the defaults below. All input forms feed the same validated connection model and renderer. Keep --from FILE and --from - as optional file/stdin inputs. Reject mixed connection input forms rather than silently overriding URI fields with flags. Local options such as --listen remain available for every input form.

client.uri is an ordinary UTF-8 text file containing one standard vless:// URI and an optional trailing newline; the filename is arbitrary. It is not a new PIG configuration format. Support the reference combination only: REALITY, Vision, and TCP/RAW. Map URI type=tcp to JSON network: raw, pbk to password, sid to shortId, and pqv to mldsa65Verify. Preserve the ML-DSA verification field; never silently drop it. Reject duplicate or unsupported parameters that would change connection meaning. The mapping follows the upstream sharing specification.

On first setup, bind a socks inbound to 127.0.0.1:12345, retaining the existing PIG client endpoint default. --listen can explicitly select 127.0.0.1:8888 to match the inspected client. On repeated setup, omitting --listen preserves the existing managed client’s listener. Provide HTTP support on the same port, UDP enabled, and multiplexing disabled. Use encryption: none, the Vision flow, a Chrome fingerprint, and spiderX: /. The upstream Socks reference documents HTTP support. Validate HTTP and SOCKS independently; a TCP listener alone is insufficient.

The CLI fields map to the existing reference configuration as follows. Client paths below are relative to its VLESS outbound; server paths are relative to its VLESS inbound.

CLI field Client configuration Server configuration or export
Server --host, --port; client --server settings.vnext[0].address and .port Advertised URI endpoint; not necessarily the bind endpoint
Role-specific --listen Local inbound listen and port VLESS inbound listen and port
Client --id settings.vnext[0].users[0].id Matches settings.clients[0].id
--sni streamSettings.realitySettings.serverName Member of streamSettings.realitySettings.serverNames
Client --public-key streamSettings.realitySettings.password Derived from streamSettings.realitySettings.privateKey
Client --short-id streamSettings.realitySettings.shortId Member of streamSettings.realitySettings.shortIds
Client --pqv streamSettings.realitySettings.mldsa65Verify Derived from streamSettings.realitySettings.mldsa65Seed
Server --target No direct client field; SNI must be compatible streamSettings.realitySettings.target
Server --proxy-protocol No client field streamSettings.rawSettings.acceptProxyProtocol: true

The reference server keeps realitySettings.xver: 0: accepting a PROXY header from Nginx and forwarding a PROXY header to the REALITY target are different settings. Template defaults preserve the reference flow, transport security, logging, disabled multiplexing, server private-destination blocking, and client fingerprint. The public endpoint, bind endpoint, and target have separate roles.

Platform and service ownership. Linux uses the configured package repositories and the package account, configuration path, and unit above. macOS clients use Homebrew Xray, a mode-0600 user configuration at ~/.config/xray/pig-proxy.json, and a dedicated user LaunchAgent named com.pigsty.xray-proxy; do not take over another Xray daemon or Homebrew service. Linux setup requires root and a running systemd instance; macOS client setup requires a regular user and existing Homebrew. macOS server setup is outside this change.

New role setup explicitly starts and enables the managed service; install-only does not start or enable it. On Linux, PIG owns a bounded pig-proxy.conf systemd drop-in for asset location and capabilities. Direct binding to a privileged port requires only CAP_NET_BIND_SERVICE in the bounding and ambient sets. Reset both sets before assignment because systemd combines positive directives with the package unit. A loopback backend on an unprivileged port does not need that capability. Do not depend on permissive host sysctl settings or run Xray as root to make port 443 work.

Application and recovery. Parse arguments and preflight paths, port ownership, service conflicts, and existing configuration before mutations. Recognize managed configuration by its complete supported shape, not merely an inbound tag. Replacing a different or unsupported client configuration requires --replace --yes. Unsupported server configurations and service units using another account, an explicit different group, or extra configuration arguments are refused. Preview supports --plan, with no installation, credential creation, file writes, or restarts.

Render JSON through Go serialization. Stage protected candidates, validate them with Xray under the actual service identity, then atomically apply configuration and service changes. Compare content, ownership, and permissions before treating files as unchanged. On macOS, track persistent disablement separately from loaded state, enable the managed LaunchAgent on start, and restore both states on failure. Save original content, ownership, mode, service enablement, and running state for bounded rollback. Check service health and connectivity before publishing shell aliases or connection exports. Client setup requires HTTP 204 from https://www.google.com/generate_204 through both HTTP and SOCKS, so server outbound access to this endpoint is part of the client acceptance condition. An application failure returns nonzero and reports which recovery steps succeeded; an installed package may remain.

Never delete or automatically stop V2Ray. A port conflict stops preflight and produces a migration instruction. Existing V2Ray configuration remains available for operator-controlled rollback. Retain po, px, and pck for client convenience, including lowercase proxy variables needed by build tools. Activating those variables in the calling shell still requires sourcing the generated shell file and invoking po; a child process cannot modify its parent shell.

Secrets and exports. --export FILE writes the explicitly requested connection export with mode 0600 and no server private key or ML-DSA seed. --export - explicitly delivers one complete URI on stdout, with non-secret setup diagnostics on stderr. This credential-export mode requires text output; reject JSON/YAML combinations before mutations instead of putting the URI inside a structured result. Ordinary setup output remains redacted. Command-line input is supported even though the caller’s shell history or process inspection can retain those supplied credentials; file and stdin input remain available when that matters.

Treat the whole URI as a credential, including its UUID and authentication fields. Default logs, plans, JSON/YAML results, and failure details contain only redacted summaries and export paths. Generate and derive key material in process. Capture validator output privately: upstream configuration errors can include the invalid secret value. Never use a generic subprocess capture as a safe credential boundary. Re-exporting to an existing protected file containing the same canonical URI succeeds without rewriting it. A different existing export is a conflict; never silently overwrite it or print its contents in a diagnostic.

Ingress and verification boundaries. Direct mode is the default one-command server setup. The explicit --proxy-protocol backend mode requires loopback binding and a trusted pre-existing frontend. PIG does not rewrite Nginx or firewall/cloud rules in this change. A busy public port causes a clear failure instead of stopping its owner. Backend readiness does not prove public ingress; server setup reports local checks and leaves external reachability pending until a client handshake and HTTPS request succeed.

Generating equivalent Xray configurations includes all of the reference pair’s protocol and authentication fields. Reproducing the entire reference deployment also requires Nginx’s SNI map, its PROXY-enabled website backend, and OS-specific service registration. This implementation manages Xray and its own service settings; it does not claim to recreate that Nginx composition from a server URI. Configuration equivalence does not imply byte-for-byte preservation of tags, field order, or installation-specific names.

The 2026-10-16 compatibility refinement accepts the complete marker-free macOS plist generated by the earlier renderer when its configuration and binary paths match. A management comment alone is insufficient. Edited or foreign files require explicit replacement. Linux recognition is also limited to the complete generated capability variants. After the one-time comment migration, identical execution must preserve the running service.

The marker-free fixture is generated by executing the earlier renderer. That source file is byte-identical to the publicly available v1.9.0 source at 9eb54888. This is old-source-format evidence, not verification of a particular released binary.

Consequences

Users get two setup operations connected by one standard URI, or supply client parameters directly, with matching generated parameters instead of manual JSON editing. No connection file is required. Both new clients and the historical VMess form default to 127.0.0.1:12345 on first setup. Direct servers default to the public port on all IPv4 interfaces; PROXY-protocol backends default to 127.0.0.1:9443. Repeated setup preserves existing listeners when --listen is omitted. Configuration and service management remain explicit about role, OS, ownership, and external ingress.

The implementation uses cmd/build.go and focused files under cli/build. It reuses the existing plan, output, confirmation, privileged-write, and logging helpers.

Verification and evolution

Fixed source commits and regression tests for the committed v1.10.0 refinements cover: complete legacy service recognition. These commits do not establish a release boundary. The v1.9.0 runtime evidence below covers the original implementation.

The implementation is recorded in d090b7c, with ownership and service-state fixes in 5d8660c. Tests cover role syntax, equivalent input forms, redaction, listener defaults, credential reuse, export conflicts, protected files, capability selection, and plans. Full Go tests, vet, static analysis, dead-code and complexity checks, and repeated command/build race tests passed locally.

Runtime checks used Xray 26.3.27:

Environment Verified behavior
Fresh Debian 13 ARM64 client Package installation; service running as xray; HTTP and SOCKS health checks; unchanged configuration and PID on repeat setup; rollback after a wrong UUID
Fresh Debian server Direct privileged-port listener; 0640 configuration and 0600 export; stable credentials, files, and PID on repeat setup; rollback after export failure
Existing Homebrew Xray on macOS Isolated LaunchAgent; HTTP/SOCKS connectivity; unchanged reruns; restoration of configuration and persistent disabled state after failure
Existing reference server Read-only export and client tests left its configuration hash and PID unchanged

The new server’s exported URI completed REALITY/Vision connections with ML-DSA verification. HTTP and SOCKS requests to a reachable TLS site returned HTTP 200. Its network could not reach the Google health endpoint, so full client setup for that pair failed and rolled back. Full client setup passed against the existing reference server, through its Nginx/PROXY ingress.

Runtime coverage did not include RPM installation, a fresh Homebrew installation, UDP forwarding, other Xray versions, or deployment of a new PROXY-protocol frontend.

Current status

Released in v1.9.0. Nginx ingress automation and credential rotation remain future work.

14 - Bootstrapping a Pigsty Controller as a Recoverable Transaction

Why pig sty boot resolves sources before elevation, separates hard failures from finishing warnings, and restores repositories after failed setup.

Decision date: 2026-08-14
Status: Implemented and released in pig v1.8.0.
Current reference: pig sty boot
Scope: Preparing a Pigsty controller and its package sources; not deploying a database cluster.

Decision

pig sty boot should be one native, failure-aware controller bootstrap workflow. It resolves an explicit source before privilege elevation, prepares online or offline repositories, installs only the required controller packages, proves Ansible is usable, and performs bounded finishing checks.

Repository replacement is transactional: definitions are backed up and restored when package setup fails. Optional conveniences may warn, but invalid explicit input, package failures, and an unusable final Ansible environment are hard failures.

Context

The previous command delegated to a shell bootstrap script. That made source selection, download, sudo boundaries, repository rollback, error classification, and structured automation difficult to observe. A present ansible-playbook binary could also be mistaken for a usable environment even when its Python dependencies were missing.

Offline installations added another ambiguity: an already ready controller may still need a local repository prepared for later nodes.

Alternatives considered

  • Continue invoking the legacy script. Rejected because PIG could not own the transaction or explain partial failure reliably.
  • Require the entire command to start as root. Rejected because explicit downloads and source validation do not need privilege and should happen before one bounded elevation.
  • Treat an Ansible binary as readiness. Rejected because the executable may use a Python environment missing required modules.
  • Skip repository work when Ansible is ready. Rejected because staging an explicit offline source is an independent requested effect.
  • Make every finishing check fatal. Rejected because locale convenience, localhost SSH repair, or tree initialization can fail without invalidating an otherwise usable controller.

Contract

  • explicit local paths and URLs are validated and never silently fall back to online mode;
  • automatic offline sources must pass ownership and permission checks;
  • a committed local repository can take precedence over a selected package;
  • download and restricted archive extraction use native bounded implementations;
  • offline bundles require a pigsty/repo_complete sentinel; additional roots are preflighted and renamed before pigsty, while conflicts and interrupted residue fail closed for manual cleanup;
  • privilege elevation happens once after source resolution and can be disabled or made non-interactive;
  • overwritten repository definitions are recoverable on setup failure;
  • readiness executes Ansible and checks the Python modules it will use;
  • the result distinguishes ready, offline, online, and existing modes;
  • hard failures and finishing warnings are separate structured fields;
  • bootstrap does not claim that Pigsty deployment has succeeded.

Consequences

The native workflow is larger than a script launcher, but its side effects and rollback state are visible. It can stage offline content on an already usable controller and gives automation a stable result without hiding optional problems.

After bootstrap, generate the Inventory, deploy the selected services, and check their status.

Verification and evolution

The native implementation landed in 222616e and was refined in 74e084e. Tests cover source precedence, permission checks, restricted extraction, sudo re-exec, repository rollback, locale recovery, Ansible/Python readiness, localhost SSH, initialization, warning classification, and structured results. The release page records delivery in v1.8.0.

The offline-bundle refinement in 8fa3abb preserves multiple validated top-level repositories and retains the repo_complete marker. It shipped in v1.8.1.

Current status

pig sty boot prepares the controller. Run deployment separately after configuring the Inventory. See pig sty for source modes, environment controls, and next steps.

15 - Install the Pigsty Repository Key Automatically

Prepare the Pigsty key best-effort, reuse existing files, and fall back with a warning unless repository metadata explicitly names a key.

Decision date: 2026-10-04
Status: Released in v1.9.0.
Current reference: pig repo
Scope: Installing the existing embedded Pigsty public key during repository configuration.

Decision

When a selected module contains an available pigsty-infra or pigsty-pgsql repository, prepare the embedded Pigsty public key before changing repository definitions. Apply this in the shared configuration workflow so repo add, repo set, and callers that reuse it agree. Reuse an existing key file. Automatic installation is best-effort; an explicit signing-key reference in selected repository metadata makes installation failure fatal.

Context

The source baseline installed the key only when a caller requested GPGCheck, as native sty boot does. Ordinary repo add/set therefore omitted a public key already shipped inside PIG.

The key is needed by both Pigsty repositories. Their membership spans pigsty, infra, pgsql, and the default all selection, so checking only the literal argument pigsty would miss normal usage.

APT does not discover files under /etc/apt/keyrings automatically. A repository must reference the prepared file with signed-by; otherwise a successful write still leaves NO_PUBKEY warnings during cache refresh.

Removing signed-by during fallback can conflict with a retained definition of the same APT source. Keep the default reference stable across successful preparation and fallback. Explicit metadata names a required key of its own; preparing an unrelated default file cannot satisfy it.

Alternatives considered

  • Download the default key at runtime. The embedded release input already supplies it and works offline, without adding a network or external-tool dependency.
  • Install the key on every repository operation. Unrelated repositories should not cause a Pigsty key write. Resolve membership and platform availability first.
  • Fail every repository operation when automatic key installation fails. Rejected because optional key preparation should not prevent the existing compatibility mode from working. Explicit signing-key metadata remains a strict boundary.
  • Enable all signature checks at the same time. Publisher identity, RPM package signatures, and APT metadata authentication are separate contracts. Automatic key installation does not establish signing coverage for every package or mirror.

Contract

  • Validate requested modules before installing the key. A rejected replacement does not install it or move repository files.
  • Match the two Pigsty repository identifiers in resolved modules, including custom modules; the URL’s hostname alone does not select a signing identity.
  • Write the existing embedded key to /etc/pki/rpm-gpg/RPM-GPG-KEY-pigsty on EL or /etc/apt/keyrings/pigsty.asc on Debian/Ubuntu when installation is needed, with mode 0644.
  • Check for an existing regular key file first, including a symlink resolving to one; reuse it without changing its contents or permissions. If the existence check fails, attempt installation.
  • Create missing parent directories through the existing sudo fallback and use the atomic file writer. A denied destination check must still reach the privileged attempt, which checks the destination again. An occupied directory or dangling symlink is an installation error.
  • Without explicit key metadata, installation failure warns and continues. Only selected Pigsty definitions fall back to APT trusted=yes or RPM gpgcheck=0 and repo_gpgcheck=0. Fill the default gpgkey or signed-by reference on both success and failure. Do not rewrite retained repository files to implement fallback.
  • Explicit key preparation applies to the actual reference. APT signed-by accepts absolute paths to existing regular keyring files, separated by commas; install the embedded key when the default path is explicitly named. EL prepares an explicitly named default file if needed and passes gpgkey paths or literal URLs to native rpm --import.
  • Failure to prepare an explicit key is fatal before backup, repository writes, or cache refresh. Preserve its reference and never downgrade that definition. A valid custom key does not depend on the default path; an implicit-key failure in another selected repository affects only that implicit definition.
  • Successful ordinary operations preserve signature-checking settings. sty boot requests signing on successful key preparation and follows the same implicit-key fallback on failure.
  • Put fallback warnings on stderr and in repo add/set’s data.warnings; sty boot retains them in its own warnings. Implicit installation does not import into RPM’s database; explicit EL metadata does. Neither path changes APT’s global trust store.
  • Repository rollback restores definitions; already installed or imported public keys remain.

Consequences

Adding a Pigsty repository also attempts to prepare its public-key file, including on offline hosts. Ordinary key failures no longer prevent repository setup. Falling back disables signature verification for those Pigsty definitions and is reported explicitly. A successful existence check preserves the administrator’s existing file; it does not validate that file’s contents. Third-party repository trust remains unchanged. APT may retain previously authenticated indexes with a warning when a key becomes unavailable. Successful repository configuration therefore does not prove that every index was freshly downloaded. The maintained repository reference describes the resulting defaults and paths.

Verification and evolution

The automatic-key implementation and repository regression tests are recorded in source commit 56f7353. Local regression tests exercise EL, Debian, and Ubuntu on both supported architectures, default and composite selections, existing-key reuse, denied existence checks, custom modules, unavailable repositories, implicit-key fallback, and explicit-key failure before repository mutation. Existing replacement tests continue to cover rollback.

Actual CLI tests on local EL9, EL10, Debian 12, and Ubuntu 24 ARM64 guests use isolated mount namespaces. They verify installation as an ordinary user through sudo with missing key directories, exact embedded bytes and permissions, repeated execution, composite selections, parseable structured output, implicit-key failure continuing with a warning, and explicit-key failure stopping before repository replacement. Real DNF/APT refreshes use isolated caches, and the resulting indexes are queried for available packages. APT tests also verify that the installed key is referenced without NO_PUBKEY warnings and that explicit signed-by with trusted=no authenticates the metadata. These checks do not install packages or verify RPM package signatures. The guests' original repository definitions, keys, and caches remain unchanged.

Follow-up regression checks cover retained APT sources and previously authenticated caches, custom-key absence before both add and replacement, a valid custom key with an unusable default path, and mixed explicit/implicit definitions. Permission tests deny ordinary-user traversal of the key directory and verify both privileged installation and rejection of a hidden symlink. RPM import tests use an isolated copy of the guest’s RPM database.

Current status

Released in v1.9.0. See the current reference linked above for usage and platform requirements.

16 - One Catalog Entry Count for the Website and CLI

Count every catalog record consistently across the website, extension lists, availability summaries, and catalog refresh results.

Decision date: 2026-10-16
Status: Implemented in the v1.10.0 candidate, not yet released.
Current reference: pig ext catalog count
Scope: Catalog totals and default listing membership; package installation and SQL activation rules remain defined by each entry.

Decision

Use the number of catalog records as the shared extension count. Include packaged SQL utilities and retained lifecycle records. Calculate CLI totals from the catalog actually loaded or downloaded.

Context

The catalog serves package discovery as well as PostgreSQL extension discovery. A packaged SQL utility can be useful without a control file or a CREATE EXTENSION entry point. Excluding it from the default list and total makes those outputs disagree with the website and catalog file, even though an explicit name search still finds it.

Alternatives considered

  • Count only records classified as PostgreSQL extensions. Rejected because it gives the CLI and website different totals for the same catalog.
  • Add a separate public count for every entry type. Unnecessary for the current listing contract.
  • Hard-code the announced total in the CLI. Rejected because a downloaded catalog can differ from the bundled one.

Contract

  • The unfiltered default list includes every catalog record, including SQL utilities and removed records.
  • Text row counts and JSON/YAML data.count equal the number of returned records.
  • Searches count their matches; --pkg continues to count leading package records.
  • The global availability text summary uses the complete catalog count.
  • Catalog refresh results use the downloaded record count in data.extension_count and their message.
  • Website totals follow the bundled catalog. A newer downloaded catalog may have a different total.
  • Counting a record does not change its DDL requirements, lifecycle state, or platform availability.

Impact

SQL utilities appear in the default list as well as explicit searches. Output field names and types remain stable. Consumers should interpret the extension total as catalog coverage; it is not a count of extensions enabled in a database or installable on a particular host.

Verification

Fixed source commits and regression tests for the committed v1.10.0 refinements cover: catalog entry count and availability. These commits do not establish a release boundary.

The preceding classification rule is preserved in the v1.9.0 catalog source. Acceptance checks compare parsed record counts with default lists, availability summaries, and catalog refresh results. They cover packaged SQL utilities and removed records, while keeping search and package counts specific to their returned rows.

Status

Accepted on 2026-10-16 and implemented with regression tests. The v1.10.0 candidate bundles 603 records and is not yet released. Use the current reference for the counting contract and release notes for version boundaries.