publish

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

errors.go — sentinel errors for the P1.3 ZK opt-in flags.

Each Error() returns the upstream stable code from paygate-zk/docs/prds/konareef-integration-contract-v1.md § 6 (PRD 4 error taxonomy) so the CLI surface, structured logs, and downstream consumers all see the same identifier.

Package publish orchestrates the local half of `konareef pod publish` — turning a pod directory into a signed bundle ready to POST to reef-core's `/api/pods` endpoint.

Prepare composes the three existing primitives:

  • internal/pod — parse + schema validation
  • internal/canon — canonicalization + pod_hash (SHA-256)
  • internal/identity — secp256k1 ECDSA over the canonical bytes

It is deliberately offline-only: anything that needs the network (the actual POST, the install URL, the published-pod row) lives in the Submit function, so the local steps can be `--dry-run`-tested without a running reef-core.

zk_session.go — ZK-session orchestration for `konareef pod publish` per PRD 4 § 4.3.

The four publish flags (--circuit-id, --pin-circuit-vkey, --disclosure-policy, --zk) carry distinct semantics with a fail-closed combination matrix. ValidateZKFlags is the single parse-time gate; downstream code can assume any ZKFlags value passing through it is structurally valid.

PreflightPinCheck is the pre-session vkey pin check per PRD 4 § 4.3.1. It is invoked AFTER ValidateZKFlags and BEFORE any network submission. A pin failure halts the session with vkeystore.ErrCircuitPinMismatch — no fold request reaches PayGate.

SealDisclosurePolicy is the witness-time enforcement point per PRD 4 § 4.3.3. Once called, the witness's disclosure policy is irrevocable; a second call returns ErrDisclosurePolicySealed.

EnsureSaltForTypeD provisions a 32-byte salt for the pod's lineage_id and persists it via the supplied saltstore backend for Type-D publishes (PRD 4 § B.1). No-op for Type-C and non-ZK.

Index

Constants

This section is empty.

Variables

View Source
var ErrAnchorBroadcastUnsupported = errors.New("ERR_ANCHOR_BROADCAST_UNSUPPORTED")

ErrAnchorBroadcastUnsupported is returned by EnsureVkeyAnchor when no on-chain anchor exists yet for (circuit_id, vkey_sha256) AND the BSV wallet integration has not landed. The publisher must wait for P1.8 (BSV wallet wiring) before --pin-circuit-vkey can initiate a fresh anchor transaction.

View Source
var ErrCircuitIDRequiredForPinCircuitVkey = errors.New("ERR_CIRCUIT_ID_REQUIRED_FOR_PIN_CIRCUIT_VKEY")

ErrCircuitIDRequiredForPinCircuitVkey is returned at flag-parse time when --pin-circuit-vkey is set without --circuit-id. The runtime path fetches the discovery manifest and looks up the vkey sha256 for flags.CircuitID before anchoring, so an empty CircuitID is internally inconsistent — there is no manifest entry to anchor against. Terminal; the publisher must supply --circuit-id before re-invoking. (Round-6 B1.)

View Source
var ErrDisclosurePolicySealed = errors.New("ERR_DISCLOSURE_POLICY_SEALED")

ErrDisclosurePolicySealed is returned when SealDisclosurePolicy is called twice on the same witness — once sealed, the witness's disclosure policy is irrevocable per PRD 4 § 4.3.3.

View Source
var ErrDisclosurePolicyViolation = errors.New("ERR_DISCLOSURE_POLICY_VIOLATION")

ErrDisclosurePolicyViolation is returned by konareef verify when the --disclosure-policy assertion does not match the bundle's embedded `disclosure` field. PRD 4 § 6 stable code.

View Source
var ErrInvalidDisclosurePolicy = errors.New("ERR_INVALID_DISCLOSURE_POLICY")

ErrInvalidDisclosurePolicy is returned when --disclosure-policy is supplied with a value other than the literal upper-case strings "C" or "D". Terminal.

View Source
var ErrPaygateZKDomainMissingPrefix = errors.New("ERR_PAYGATE_ZK_DOMAIN_MISSING_PREFIX")

ErrPaygateZKDomainMissingPrefix is returned by PublisherBaseDomain when the discovery manifest's `paygate_zk_domain` is not the expected service host (must start with `paygate-zk.`). The contract is documented at PRD 2 § 5.3 and internal/publish/discovery.go: the value MUST be the bare service host (e.g. `paygate-zk.example.com`), not a publisher base domain.

View Source
var ErrZkRequiresCircuitID = errors.New("ERR_ZK_REQUIRES_CIRCUIT_ID")

ErrZkRequiresCircuitID is returned at flag-parse time when --zk is set without --circuit-id. Terminal; the publisher must supply --circuit-id before re-invoking.

View Source
var ErrZkRequiresDisclosurePolicy = errors.New("ERR_ZK_REQUIRES_DISCLOSURE_POLICY")

ErrZkRequiresDisclosurePolicy is returned at flag-parse time when --zk is set without --disclosure-policy. Terminal; the publisher must supply --disclosure-policy {C|D}.

Functions

func EnsureSaltForTypeD

func EnsureSaltForTypeD(flags ZKFlags, spec *pod.Spec, backend saltstore.Backend) ([16]byte, [32]byte, error)

EnsureSaltForTypeD provisions a 32-byte salt for the pod's lineage_id and persists it via the supplied saltstore backend. Idempotent: if a salt is already stored for the lineage_id, the stored value is returned unchanged.

No-op for Type C (the salt rides in the public bundle and is not stored separately) and for non-ZK publishes.

When backend is nil (the publisher's environment has no usable salt-storage backend), returns saltstore.ErrSaltStorageUnavailable so the caller can halt the publish before any submission.

Spec: paygate-zk @ konareef-integration-contract-v1.md § 4.3.3 + P1.7.1 plan (saltstore.Resolve).

func PreflightPinCheck

func PreflightPinCheck(ctx context.Context, resolver Resolver, flags ZKFlags, manifest DiscoveryManifest) error

PreflightPinCheck is the pre-session vkey pin check per PRD 4 § 4.3.1. When flags.ZK is true:

  1. Look up the manifest's vkey_sha256 for flags.CircuitID; absent → return a wrapped error (the manifest does not advertise the requested circuit).
  2. Resolve the vkey through the resolver (cache → Tier 1 → Tier 2); the resolver itself enforces the SHA-256(vkey) == vkey_sha256 contract via vkeystore.VerifyPin.
  3. On vkeystore.ErrCircuitPinMismatch: propagate; the caller MUST halt with no submission.
  4. On vkeystore.ErrVkeyUnavailable: propagate; the caller MUST halt — verification cannot proceed without the vkey.

When flags.ZK is false this function is a no-op (legacy publish does not pin to a circuit).

func PublisherBaseDomain

func PublisherBaseDomain(zkServiceHost string) (string, error)

PublisherBaseDomain converts a discovery `paygate_zk_domain` service host into the publisher base domain expected by vkeystore.ResolveRequest.PublisherDomain. The conversion is a pure case-insensitive `paygate-zk.` prefix strip; the remainder MUST be a non-empty base domain (the caller passes it to vkeystore.ValidatePublisherDomain, which applies the strict DNS-hostname grammar).

Returns ErrPaygateZKDomainMissingPrefix when zkServiceHost is empty or does not start with `paygate-zk.` (case-insensitive). The prefix-stripped remainder is also checked for non-emptiness so a pathological input like `paygate-zk.` alone is rejected.

MR !21 round-2 B2 (note 680): fixes the live ZK publish + standalone pin flows when reef-core returns `paygate_zk_domain= "paygate-zk.example.com"`. Before this helper the value was passed raw to the resolver, which rejected it with ErrDomainAlreadyPrefixed.

func SealDisclosurePolicy

func SealDisclosurePolicy(flags ZKFlags, w *Witness) error

SealDisclosurePolicy is the witness-time enforcement point per PRD 4 § 4.3.3. It is the single permitted place to set witness.DisclosurePolicy. Once sealed, the witness's policy is irrevocable for the session — a downstream serialiser cannot subvert the publisher's choice.

When flags.ZK is false this function is a no-op (legacy publish has no witness).

func ValidateZKFlags

func ValidateZKFlags(flags ZKFlags) error

ValidateZKFlags enforces the parse-time fail-closed matrix per PRD 4 § 4.3.4. Returns one of:

  • nil — the combination is valid; the caller may proceed.
  • ErrZkRequiresCircuitID — --zk without --circuit-id.
  • ErrZkRequiresDisclosurePolicy — --zk without --disclosure-policy.
  • ErrInvalidDisclosurePolicy — --disclosure-policy with a value other than the literal upper-case strings "C" or "D".
  • ErrCircuitIDRequiredForPinCircuitVkey — --pin-circuit-vkey without --circuit-id (round-6 B1). The runtime path fetches the discovery manifest and looks up VkeySha256For(flags.CircuitID) before anchoring; an empty CircuitID is internally inconsistent.

Non-ZK invocations are accepted unchanged: --circuit-id is usable standalone. --pin-circuit-vkey is NOT usable standalone — it requires an explicit --circuit-id (round-6 B1).

Types

type AnchorState

type AnchorState int

AnchorState reports the result of EnsureVkeyAnchor.

const (
	// AnchorAlreadyExists means the on-chain anchor for
	// (circuit_id, SHA-256(vkey)) is already recorded; no new
	// broadcast is needed.
	AnchorAlreadyExists AnchorState = iota

	// AnchorBroadcasted means a new anchor transaction was
	// constructed, signed, and broadcast. Not reachable in v1; the
	// BSV wallet integration is owed by P1.8.
	AnchorBroadcasted
)

func EnsureVkeyAnchor

func EnsureVkeyAnchor(ctx context.Context, anchorBackend vkeystore.Tier2AnchorBackend, circuitID string, vkeyBytes []byte) (AnchorState, error)

EnsureVkeyAnchor implements the --pin-circuit-vkey behaviour:

  1. Look up the existing on-chain anchor hash for circuitID via the Tier-2 backend.
  2. If an anchor exists AND SHA-256(vkeyBytes) == anchor hash: return AnchorAlreadyExists (the flag is a no-op).
  3. If an anchor exists but the hash MISMATCHES: return vkeystore.ErrCircuitPinMismatch — the publisher must re-anchor or upgrade the circuit; do not proceed.
  4. If no anchor exists (ErrAnchorNotFound): return ErrAnchorBroadcastUnsupported — the broadcast path is owed by a future PRD.
  5. Any other Tier-2 error: propagate verbatim.

The function never has side effects on the chain in v1.

func (AnchorState) String

func (s AnchorState) String() string

String returns a stable lowercase identifier for logging.

type DiscoveryManifest

type DiscoveryManifest interface {
	// PaygateZKDomainHost returns the host portion (no scheme) of the
	// PayGate ZK service domain.
	PaygateZKDomainHost() string

	// VkeySha256For returns the vkey_sha256 hex string committed in
	// the manifest for circuitID, or ("", false) if the circuit is
	// absent.
	VkeySha256For(circuitID string) (string, bool)
}

DiscoveryManifest is the slice of the PRD 2 § 5.3 manifest this package consumes. The caller fetches and decodes the full manifest; this package only needs the paygate-zk domain + the circuits list.

func FetchDiscoveryManifest

func FetchDiscoveryManifest(ctx context.Context, serverURL string) (DiscoveryManifest, error)

FetchDiscoveryManifest GETs <serverURL>/api/discovery and decodes the slice consumed by PreflightPinCheck.

type PreparedPod

type PreparedPod struct {
	Handle         string
	PodName        string
	PodVersion     string
	PodHash        [32]byte
	CanonicalBytes []byte
	Signature      []byte
	PublicKeyHex   string

	// Spec is the parsed pod.toml. Carried through so the CLI can
	// derive the lineage_id for Type-D salt provisioning without
	// re-parsing the manifest. Set by Prepare.
	Spec *pod.Spec
}

PreparedPod is the local result of `Prepare`: the canonical manifest bytes, the SHA-256 pod_hash they produced, and the publisher's signature over those bytes — plus the metadata fields (`handle`, `pod_name`, `pod_version`, `publisher_pubkey`) that reef-core needs to register the pod.

Submit consumes one of these directly; the CLI's `--dry-run` mode just renders it.

func Prepare

func Prepare(podDir string, id *identity.Identity) (*PreparedPod, error)

Prepare runs the offline half of `konareef pod publish`:

  1. Read `<podDir>/pod.toml`.
  2. Parse + validate against the v0.1 schema. Any validation issue is fatal — `pod publish` is not the place to discover schema bugs.
  3. Canonicalize the manifest + the pod-directory contents (the synthetic `[_files]` section binds every other file in podDir into pod_hash, defeating post-sign file-swap attacks).
  4. `pod_hash = SHA-256(canonical_bytes)`.
  5. Sign the canonical bytes with `id`. The Sign primitive hashes internally, so the signature is over pod_hash (matches the publisher-signing-design v0 §"Stage 2" algorithm).

The returned PreparedPod is ready for Submit or `--dry-run` rendering; no network call is performed here.

type Resolver

type Resolver interface {
	Resolve(ctx context.Context, req vkeystore.ResolveRequest) (*vkeystore.Vkey, error)
}

Resolver is the narrow interface PreflightPinCheck consumes from internal/vkeystore. Declaring it inline here lets the publish package depend on the abstraction (and the test wire a fake without a full vkeystore.Resolver).

type SubmitOpts

type SubmitOpts struct {
	PublicBundle     bool     // WI-P0-012
	CircuitID        string   // PRD 4 § 4.3.1
	ZkEnabled        bool     // PRD 4 § 4.3.4
	DisclosurePolicy string   // PRD 4 § 4.3.3 — "C" or "D"
	Witness          *Witness // PRD 4 § B.1 — sealed witness for v2 / Type-D path; nil for v1 / non-ZK
}

SubmitOpts groups the post-P1.0 publish-time flags. The zero value represents a legacy publish (no opt-ins). Each non-zero field maps 1-1 to a `submitRequest` field of the same name.

Round-4 B1: SubmitOpts gains a Witness *Witness field. For ZK publishes the caller sets Witness to the sealed witness after SealDisclosurePolicy has succeeded, and Submit threads the witness's lineage commitment into the wire body (Type-D) and into the v2 packer (PRD 4 § B.1). Witness is nil for v1 / non-ZK publishes; in that case Submit emits the byte-identical pre-P1.3 wire body.

Data-path discipline (PRD 4 § B.2): Witness.LineageID flows into the publish wire body; Witness.Salt stays LOCAL on the publisher — consumed only by the v2 bundle packer for index / value_hash derivation, then discarded; NEVER serialised into the public bundle output or the submit wire body.

type SubmitResponse

type SubmitResponse struct {
	InstallURL   string    `json:"install_url"`
	RegisteredAt time.Time `json:"registered_at"`
}

SubmitResponse is the 201 body from `POST /api/pods`.

func Submit

func Submit(serverURL string, p *PreparedPod, opts SubmitOpts) (*SubmitResponse, error)

Submit POSTs a PreparedPod to `<serverURL>/api/pods`. On 2xx it returns the parsed install URL + registration timestamp. Any non-2xx status, network failure, or unparseable response is a fatal error — there is no retry policy here; the CLI is the human-facing wrapper that interprets failures.

SubmitOpts carries the opt-in flags; the zero value preserves pre-P1.3 wire shape exactly.

type Witness

type Witness struct {
	// DisclosurePolicy is the per-session policy sealed by
	// SealDisclosurePolicy. After Sealed is true, it MUST NOT change.
	DisclosurePolicy string

	// Sealed is true once SealDisclosurePolicy has been called. A
	// second seal call returns ErrDisclosurePolicySealed.
	Sealed bool

	// LineageID is the 16-byte pod lineage identifier provisioned by
	// EnsureSaltForTypeD on Type-D publishes. Zero for Type-C and for
	// non-ZK publishes.
	LineageID [16]byte

	// Salt is the 32-byte CSPRNG-derived salt persisted by the
	// saltstore backend for Type-D publishes. Zero for Type-C and
	// non-ZK. Consumed LOCALLY by the v2 packer per PRD 4 § B.1 then
	// discarded; NEVER serialised over the wire (§ B.2).
	Salt [32]byte
}

Witness is the publisher's intent envelope passed into PayGate ZK proof construction. Reduced to the fields this PRD touches; the full payload (P, R, M_in, M_out, T_log_records) is the V2 packer's concern (P1.2).

type ZKFlags

type ZKFlags struct {
	// CircuitID is the value of --circuit-id. Empty when absent.
	// Mandatory when ZK is true (PRD 4 § 4.3.4 + § 4.3.1).
	CircuitID string

	// PinCircuitVkey is the value of --pin-circuit-vkey. True
	// triggers the on-chain anchor flow per PRD 4 § 4.3.2.
	PinCircuitVkey bool

	// DisclosurePolicy is the value of --disclosure-policy,
	// normalised to upper-case at parse time by the caller. Valid
	// values: "C", "D". Empty when absent. Mandatory when ZK is true
	// (PRD 4 § 4.3.4 + § 4.3.3).
	DisclosurePolicy string

	// ZK is the value of --zk. True activates the full ZK path:
	// witness construction, Spartan proof generation, v2 bundle
	// emission. Sets published_pods.zk_enabled = true on reef-core.
	ZK bool
}

ZKFlags captures the four ZK-related publish flags. The zero value represents a legacy publish (no ZK opt-in).

Jump to

Keyboard shortcuts

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