Skip to content

Ingest and staged publish paths, reliable spool, hardening - #2

Open
pbuncic wants to merge 128 commits into
masterfrom
fix/release-hardening
Open

pbuncic wants to merge 128 commits into
masterfrom
fix/release-hardening

Conversation

@pbuncic

@pbuncic pbuncic commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Ingest and staged publish paths, reliable spool, hardening

Merges fix/release-hardening into master: 119 commits, 241 files, +30k/−12k lines, about half of them tests. It carries the staged-ingest work that bits.cern.ch production runs on cvmfs-bits-01, plus the clean-up and hardening done for the release. master has no commits that are not on this branch, so the merge is conflict-free (it can fast-forward).

What changes

Publish paths. A job chooses how its package reaches the repository (publish_path); one prepub serves several paths at once.

  • ingest (production default): the tar goes to cvmfs_server ingest on a mountless publisher, through the gateway. Missing parent directories are created through the gateway; a per-job --direct-s3 sends data objects straight to S3, and the publisher reports the objects it uploaded over an inherited pipe. Cancelling a publish kills the whole cvmfs_server process group.
  • staged: the producer stages objects and a catalog under an S3 prefix; prepub promotes them into the CAS server-side, takes the lease and grafts the catalog.
  • coarse: a build's packages accumulate and are published in one commit by a finalize job, with a CAS existence check before the catalogs are committed. On by default; local mode is never coarse.
  • DirectGraft commits go to the gateway's dedicated endpoint POST /api/v1/leases/<token>/graft (cvmfs PR #4296), replacing the "direct_graft" body flag that never landed upstream. This includes Valentin Volkl's change and gateway CI from lease: target dedicated gateway graft endpoint for DirectGraft #1, which this PR supersedes.

Reliable spool. An accepted job is published eventually unless it fails for a permanent reason.

  • Transient failures retry with backoff (1, 2, 4, 8, 16 min, then every 30 min) within retry_window (default 24 h). Conflicts, invalid archives and operator aborts fail at once.
  • Uploads are refused up front above max_tar_size_gib (default 10) or when the spool would drop below spool_min_free_gib; /health advertises the limit. The same limit caps a single file in the tar on the streaming path; the non-streaming paths keep a 1 GiB per-file cap.
  • Payloads are deleted when a job reaches a terminal state. A job whose content is already published (same path and build hash) completes without a second commit.
  • Admission and prefetch are charged by tar size, so large packages serialise. An operator restart does not spend a job's retry budget.

Security.

  • API requests are HMAC-signed (method, route, body, nonce) with per-route body caps; OIDC issuers without an audience are refused.
  • Stratum 1 receivers use per-node broker keys (S1_NODE_KEY, formerly PREPUB_NODE_KEY). Revocation is persisted in <spool>/revoked-nodes.json and can be lifted with cvmfs-prepub revoke --undo (/api/v1/control/unrevoke).
  • Stricter discovery (CA, proxy, node-ID validation, only receivers serving the repository) and provenance checks; the signed provenance record is kept with the job (provenance-record.json).
  • Publishes are confined to the job's namespace; local-mode extraction and request input are validated more strictly.

Distribution. Receivers pull from the publisher: one --receiver-stratum0-url (the publisher base URL) serves both the object pull and the post-commit root-catalog pull, which now works and is hash-verified. Probes retry before a receiver is skipped.

Catalogs. The writer passes cvmfs_swissknife check (nested catalog sizes, link counts, split roots) and uses CVMFS's hash-algorithm IDs, suffixes and symlink/chunk flags.

Observability. A measurement record per publish (/api/v1/measurements), /api/v1/published for "is this path published, by which build hash", Prometheus gauges for spool states, retries, spool disk, host load and memory, a startup tuning banner, and cvmfs-prepub --version.

Operations. install.sh gains update (keeps configuration) and uninstall, both acting on the roles installed on the host, plus --user and --spool-dir. INSTALL.md covers new nodes and moving prepub off the gateway node.

Removed (never wired into a working path): warm quorum, /s1/catchup, /s1/{txn}/lease, GC pins and prepubctl.

Docs and licence. README, INSTALL and REFERENCE rewritten and checked against the code; Apache-2.0 headers throughout.

Before deploying

  • Clients must sign requests (bits already does).
  • Rename PREPUB_NODE_KEY to S1_NODE_KEY and give each receiver its own broker key.
  • Receivers: drop --data-addr, --data-host, --disk-headroom, --session-ttl, --tls-cert, --tls-key and --warm-quorum from unit files; the receiver no longer starts with them. Set --receiver-stratum0-url to the publisher base URL.
  • Against a gateway without the graft endpoint, set gateway.direct_graft: false.
  • Defaults: chunking fixed at 24 MiB, Stratum 1 pre-warming opt-in (--prewarm), --job-timeout off (retries and stall detection replace it), max_tar_size_gib 10 — raise it where large packages need it.
  • /api/v1/measurements needs no authentication.

Testing

  • go build ./..., go vet ./... and go test ./... pass at e1b54ea, including the httpsig interop test against the current bits client.
  • The staged-ingest line runs in production on cvmfs-bits-01 (LCG, Key4hep, ATLAS, ALICE via ingest); retries carried the ALICE O2 publish through a multi-hour gateway outage without resubmission.
  • End-to-end run of the hardening commit on cvmfs-testbed (publish, receiver pull, revoke/undo) before merging.

pbuncic and others added 30 commits June 19, 2026 21:28
Removed outdated sections on phased deployment and roadmap from REFERENCE.md.
- Catalog merge attributed to the gateway (cvmfs_receiver), not prepub
- Lease API marked as upstream cvmfs_gateway endpoints; PUT /leases/{token} unsupported (405); removed bogus 2-min TTL
- Path-scoped leases (partial namespace), not full-catalog locks
- §10 GC: bits only pins + cleans temp; cvmfs_server gc reclaims namespace
- §4.1 table: entry point/identity = publisher node (not S0); added Privilege row; removed worker-parallelism row
- §8.3 sequence: warm quorum before the catalog flip (three-phase order)
- Removed hard-link section, Bloom references, push-as-current-option
- secret -> hidden (dropped Google-Docs analogy); generic example names
- Distribution hierarchy O(10) S1 -> O(10) squid -> O(1000) workers
…; clarify lease renewal (405) constraint

- catalog table: add hash, mode, mtimens, uid, gid, xattr (full 16 columns)
- document all six tables (catalog, chunks, nested_catalogs, bind_mountpoints, statistics, properties); correct statistics to the 24 self_*/subtree_* counters; note schema 2.5 / revision 7
- flags: add FlagFileSpecial=16, FlagFileExternal=128, bind-mountpoint/hidden bits, compression bits 11-13; xattr signalled by non-NULL BLOB
- REST API request tables: add preload_exe, preload_paths (both modes)
- job-status response table: add the 14 missing fields (timing, n_new_objects, new_root_hash, distribution_*, failed_at_state, tar/tag metadata)
- §8.3 sequence: warm quorum before the catalog flip (three-phase order)
- §9.3: lease renewal PUT returns 405 on stock gateway (no renewal); lease bounded by max_lease_time; --lease-retry-max must exceed it
Adapt the gateway client from the body-flag graft API (cvmfs PR #4268,
"direct_graft":true in the commit body) to the dedicated graft endpoint
(cvmfs PR #4296, POST /api/v1/leases/<token>/graft). PR #4268 was never
merged; PR #4296 is the shape that landed, and it selects the DirectGraft
fast path by request type rather than a wire flag.

The commit body is identical for both endpoints, so DirectGraft now only
changes the target URL: Commit and CommitFinalizeOnly route to .../graft
when set, and the now-unused "direct_graft" field is dropped from the
body. The URL/body construction that was duplicated across both methods
moves into gatewayCommitURL/gatewayCommitBody helpers.

The --gateway-direct-graft flag and the DirectGraft field are unchanged;
only the underlying wire mechanism moved. REFERENCE.md documents the new
endpoint and notes it requires the graft support from cvmfs PR #4296
(the standard lease/payload/commit endpoints still need no gateway
changes).

Assisted-by: Claude (claude-opus-4-8)
A build job can reserve its target CVMFS namespace BEFORE the (expensive) build
so a taken namespace fails the job immediately instead of after hours of work.
A CVMFS gateway lease can be taken on a path that does not yet exist, so this is
a pure check: acquire the lease in a single attempt (TryAcquireOnce — no
path_busy retry) and release it right away. prepub re-acquires the lease
normally at publish time, so nothing is held across the long build.

Responses: 204 reservable, 409 namespace taken (path_busy), 400 bad body,
502 gateway error. Gateway-only: in single-host (local) mode there is no shared
lease to conflict on, so it returns 204. Authenticated like the jobs routes.
Verified: go build ./cmd/prepub/... and go vet ./internal/api/... clean.
A package/version publishes once. With DirectGraft the receiver rejects a
re-publish of an existing subtree with a generic "merge_error"
(TryGraftNestedCatalog: "invalid attempt to graft nested catalog into
existing directory"). Two improvements, both prepub-side:

* Add cvmfscatalog.PathExists: walks nested catalogs from the current root
  to decide whether a publish path already exists in the repository.
* /reserve now rejects an already-published path up front (409) so a
  duplicate fails fast before it wastes a build; the lease probe alone
  cannot catch this (a lease on an existing path is granted).
* On a DirectGraft commit merge_error, confirm existence and surface a
  clear terminal 'already published' error instead of the cryptic reason.

Different package subdirs still commit concurrently and both succeed
(DirectGraft grafts onto the current gateway root, serialised per-repo by
the gateway); only a true duplicate fails, and it is never retried.
Under rapid sequential commits the receiver grafts against the base manifest it
fetches from stratum0, which lags the gateway's committed state — so the next
package can graft onto a base missing the parent dir the previous commit just
created, yielding a spurious merge_error (observed with all-already_installed
runs where commits fire ~100ms apart).

Add waitForManifestPropagation: while the per-repo commit lock is held, block
until stratum0's published root advances past the base we committed against,
then release so the next commit sees a current base. Applied after both the
content commit and the ensureParentDirs (mkdir-p) commit. It is a barrier, not
a retry: bounded (60s), and on timeout it logs loudly and proceeds (the commit
already succeeded) rather than hanging. Replaces the single post-commit
manifest fetch (which also recorded j.NewRootHash).
The serialize-until-published barrier only runs after a *successful* commit,
so it cannot prevent the cold-start burst on a fresh arch tree: many jobs
concurrently commit content against a base whose parent dirs (created by
ensureParentDirs under a *separate* lock acquisition) are not yet committed or
propagated, all fail merge_error, and the publish loop exits on the first
failure (no retry).

Acquire the per-repo commit lock BEFORE ensureParentDirs (Phase 2.65) and hold
it through the pre-commit lease + SubmitPayload (2.7) and the content commit +
barrier (Phase 4), so each package's parent-dir creation and content graft form
one serialised, fully-propagated unit. ensureParentDirs no longer takes the
lock itself. Root-level publishes still acquire at the pre-Phase-3 position via
a commitLockHeld guard. Lock acquisition is factored into acquireCommitLock.
…Phase 1)

Projects []cvmfscatalog.Entry into the canonical ingestsql SQLite descriptor
(schema_revision 4: dirs/files/links/deletions/properties, copied verbatim from
cvmfs swissknife_ingestsql.cc). Content is referenced by unsuffixed hex hash
(single blob or ordered chunk list); files carry no xattr and dirs no ACL (bits
has none); nested from Entry.IsNestedRoot; compressed from CompAlgo.

Enforces ingestsql's fixed-chunk rule (ceil(size/24MiB) hashes) so content-
defined chunking reaching this path is rejected rather than silently miscommitted.

First step toward replacing the Go catalog builder (pkg/cvmfscatalog) with
descriptor + ingestsql. go build/vet/test pass.
…Phase 2)

Introduces the coarse, publish-at-end-of-build data path:

  * internal/buildset: per-package jobs Record their catalog entries + bits
    fingerprint into a build-scoped accumulator; Load reads them back; Assemble
    merges members into one repo-relative []Entry (prefixing package-relative
    paths and marking each package root as a nested catalog), applying the
    bits-hash dedup/conflict rule (same path+same fingerprint = idempotent;
    differing = excluded Conflict / partial success); Finalize writes one
    ingestsql descriptor (pkg/cvmfsdescriptor) and publishes the whole set in a
    single commit via cvmfs_swissknife ingestsql.
  * job.Job.BuildID groups a build's package jobs; submitJob accepts build_id
    (JSON + multipart). Empty preserves legacy per-package commit.

Unit-tested (record/load round-trip, expand+nest, dedup, conflict). go build/
vet/test pass across the module. Orchestrator accumulate-branch, the
StateAccumulated FSM state, and the POST /builds/{id}/finalize endpoint are the
remaining live-wiring step.
…R-0007 Phase 2)

Live-wiring of the buildset accumulator:

  * StateAccumulated FSM state (terminal): a coarse-publish package job whose
    objects are uploaded/pre-warmed and whose catalog entries are recorded,
    awaiting the build finalize. Registered in the spool state dirs + lookups;
    reachable from StateUploading/StateDistributing.
  * Orchestrator.Run: when j.BuildID is set (gateway mode), record the package's
    entries into the build accumulator and finish in StateAccumulated instead of
    acquiring a lease and committing. Empty BuildID keeps the legacy path.
  * POST /api/v1/builds/{id}/finalize (authenticated): assemble the build's
    accumulated members into one descriptor and publish them in a single commit
    via buildset.Finalize (ingestsql). Ingestsql runtime params (swissknife,
    -C config prefix, lease, env) come in the request body; repo + packages come
    from the members. Reports published/conflicts (validate-then-commit).
  * buildset.Member gains Repo (a build targets one repo).

go build + vet clean; job/spool/buildset/api/descriptor tests pass. (Pre-existing
unrelated failure in cmd/prepub main_test.go applyFileConfig arg count is not
touched by this change.)
Pre-existing drift: commit 4792930 (chunk min/avg/max overridable via config.yaml)
added three *int64 params to applyFileConfig but did not update the test helper,
so cmd/prepub failed to compile under 'go test'. Add the chunk fields to
applyTestVars and pass them. Test-only; no behaviour change.
…est (ADR-0007)

Testbed validation of a 2-package coarse build panicked in ingestsql
(catalog_mgr_rw.cc: 'catalog for directory ... cannot be found'): ingestsql does
not reliably auto-create the intermediate ancestor directories of a *branching*
multi-package tree — it needs them in the descriptor. Assemble now synthesises a
directory entry for every ancestor path between the build's common lease root and
each entry (package roots keep their nested marking; ancestors above the lease
root are left out — they are the graft attach point). Verified on the testbed: the
same 2-package build then published in one commit (rev 35->36) with both package
roots as nested catalogs.

Unit test TestAssembleFillsIntermediateDirs covers it.
…R-0007 Phase 2)

E2E on the testbed: the pipeline emits a package's root entry as FullPath='.'
(and paths may carry a leading './'); expand now normalises both to the package
base (previously '.' produced 'base/.'). Add cmd/prepub-finalize: a host-side
tool that Loads a build's accumulated members and runs buildset.Finalize (one
ingestsql commit). This is the deployment-correct finalize mechanism — the
containerized prepub cannot run ingestsql (no swissknife, no store mount); it
commits via the gateway, while ingestsql + the store live on the release-manager
host.
…ADR-0007)

Per review, the coarse-publish finalize is now a normal prepub job so the console
reuses its existing submit+poll machinery instead of a bespoke endpoint call:

  * job.Job.Finalize marks a payload-less job that publishes all of BuildID's
    accumulated packages in one ingestsql commit; submitJob accepts finalize=true
    (no tar) with a build_id (multipart).
  * Orchestrator.Run short-circuits finalize jobs to Orchestrator.FinalizeBuild
    (extracted, reused by the /builds/{id}/finalize endpoint too); FSM allows
    incoming→committing for them.
  * Ingest settings (swissknife path, -C config prefix, env) move to server/
    orchestrator config (IngestSwissknife/IngestConfigPrefix/IngestEnv) so the
    finalize job uses the prepub's own config rather than per-request params.

go build/vet and all 30 test packages pass.
… (ADR-0007)

Wire the coarse-publish finalize ingest settings from the CLI to the
Orchestrator (IngestSwissknife/IngestConfigPrefix/IngestEnv). Empty
--ingest-config-prefix leaves finalize disabled (finalize jobs return a clear
error). splitCSV helper parses --ingest-env into KEY=VAL entries.
…ingest

ADR-0006 (superseded): gateway returns authoritative root on acquire.
ADR-0007 (decided/implemented): coarse publish via descriptor + ingestsql —
Variant A, publish-at-end-of-build, buildset accumulator + finalize, the
ingestsql spooler-follows-upstream fix, Phase 0/2 testbed validation, and the
multi-host S3 deployment analysis.
…R-0007)

Document the coarse-publish API (build_id package jobs + finalize job), the
deployment requirements (patched cvmfs_swissknife + LD_LIBRARY_PATH, the ingestsql
gateway-client config prefix, the --ingest-* prepub flags, and the CAS=shared-store
object-flow requirement), and the bits-console PREPUB_COARSE opt-in.
BuildSubtree rewrote a bare-file tar's entries but never created a directory
entry for the nested-catalog root, so its mount-point in the parent catalog was
born nameless (empty Name) and the directory appeared empty in the client
(e.g. Modules/modulefiles/<pkg>). Synthesize the named root entry when absent,
mirroring the coarse-publish buildset.expand() behaviour. Per-package path only;
the coarse/ingestsql path already handled this.
ingestsql auto-detects the lease as the shallowest descriptor path and grafts it
into its parent, which must already exist in the repo. Using the full common
prefix breaks when it is deep and its parent is absent (a build whose paths are
all under Packages/* with no modulefiles => lease .../Packages, parent missing
=> ingestsql aborts). Synthesize ancestors up to the first path component so the
lease's parent is always the repo root. No change for normal builds whose common
prefix is already a single component (e.g. x86_64-el10).
Coarse publish via ingestsql assumes fixed 24 MiB chunk boundaries and the
descriptor emitter enforces chunk-count == ceil(size/24MiB). With coarse now the
default publish mode, default the chunker to min==avg==max==24 MiB (fixed cuts)
so it works out of the box. Per-package-only deployments that want finer dedup
can set content-defined sizes via flags or config.yaml.
…efault

The pre-commit pull announce (S1 pre-warming) was emitted whenever the broker was
configured. With no S1 receivers there is nothing to warm and a warm gate must
never block a commit, so gate the announce behind --prewarm (default off). Enable
it once authoritative receivers exist. Post-commit pull (manifests + published
broadcast) is unaffected, so receivers still converge normally.
Add an allowlist of authorized CVMFS group-root paths (--allowed-publish-prefix
/ allowed_publish_prefixes in config.yaml). reserve and submit now reject any
target that resolves outside every configured root (403), so a build cannot
reserve or publish into another group's namespace even with a valid prepub
token. path.Clean collapses traversal (..) before the check. Empty allowlist
leaves the check disabled (single-namespace deployments unchanged). Finalize is
exempt: it carries no path and only commits packages already checked at submit.

For a group whose user area is a sibling of releases/ (…/<group>/user vs
…/<group>/releases), list the group ROOT so both are covered.
…e review

Adversarial review of cvmfs-prepub (the pipeline/CAS path was already clean;
findings are in local single-host mode + the API/provenance edges):

* extractTar link-then-write (HIGH): a malicious tar could plant a symlink
  entry (x -> /etc/…) then a regular-file/hard-link entry at x or x/f — the
  lexical prefix check passed while the real open() followed the symlink,
  writing attacker content outside the CVMFS repo (as the publish user,
  often root), or reading an arbitrary host file INTO the repo via a
  hard-link source. Now every write target and hard-link source has its
  parent chain Lstat-verified symlink-free (rejectSymlinkComponents, run
  before any MkdirAll walks it) and the final element is refused if it is an
  existing symlink. The CAS/pipeline path never touches the filesystem, so
  it was never exposed.

* sanitizeID('..')=='..' (MED): buildDir(spool,'..') resolved to the spool
  ROOT, so an authenticated publisher submitting build_id='..' could write
  member records into — and, on finalize, os.RemoveAll — the whole spool.
  Empty/'.'/'..'/all-dots now map to '_invalid_'.

* OIDC (MED): ValidateOIDCToken now pins the signature algorithm family
  (never HMAC/none — alg-confusion) and, when PREPUB_OIDC_AUDIENCE is set,
  binds the token to our audience. CI OIDC issuers are global, so without an
  audience check any workflow anywhere obtained Verified=true (confused
  deputy). Audience empty = not enforced (backward compatible).

* reserveHandler (LOW-MED): body capped at 1 MiB (io.LimitReader) like the
  submit path, and req.Repo runs through broker.ValidateRepo — it previously
  reached the Stratum0 URL builder unvalidated.

* Stratum0/gateway HTTP (LOW): pkg/cvmfscatalog now uses a 60s-timeout
  client instead of http.DefaultClient (no timeout) — a dribbling Stratum0
  could otherwise hang an orchestrator goroutine indefinitely (JobTimeout
  defaults to disabled).

New tests: link-then-write and hard-link-through-symlink-source against
extractTar; build_id traversal against sanitizeID.

NOTE: go build/test could NOT be run in this environment (no working Go
toolchain in the sandbox; the build host is currently unreachable). Verified
gofmt-clean on every file and manually type-checked all touched symbols
(incl. golang-jwt/v5 WithAudience/WithValidMethods/ParserOption). go build
./... and go test ./... MUST be run on the build host before cutting the
release.
The 87-package O2 finalize put its workdir (descriptor.db + ingestsql -t
scratch) in the container /tmp via os.MkdirTemp("") — a whole-build
assembly can exceed a small tmpfs, and the resulting mkstemp ENOSPC inside
ingestsql surfaced as an unexplained SIGSEGV. Create the workdir under the
spool root instead (the sized, persistent volume; dot-prefixed, and the
spool only scans its state subdirectories so it is ignored).

Also log the tail of the captured ingestsql output when the background
finalize job fails: the guard/crash reason is only in that stderr, and the
client-facing job error is sanitized for internal failures — previously the
output was dropped entirely on this path (the empty job .output observed in
production). go test ./... green (30 packages).
Accumulated content objects are unreferenced by any published catalog until
the build's finalize commit, so storage lost between accumulation and
finalize (volume wipe on redeploy, GC) previously published a fully
browsable tree whose every file read fails with EIO — seen in production
when the O2 build's finalize was retried after a testbed rebuild.

preflightObjects() samples up to two referenced objects per member (chunked
files probe hex(chunk0)+"P", plain files hex(hash); dirs/symlinks/
deletions/empty files skipped), capped at 200 CAS.Exists probes with an
even stride so every region of the build is represented. Any miss aborts
the finalize with an error naming the first missing object and its path and
advising a re-publish. Nil CAS (non-gateway deployments) skips the check.

Unit tests cover pass, missing-object failure (named path + advice),
skipped entry kinds, and the nil-CAS path. go test ./... green (30 pkgs).
pbuncic added 30 commits August 20, 2026 12:50
PREPUB_MAX_CONCURRENT_JOBS / PREPUB_MIN_CONCURRENT_JOBS join the other
.env-driven knobs, so the job-concurrency ceiling — the parallelism term
in the throughput-bound window — can be swept without a redeploy. Both
already appear in the startup tuning banner.
CI OIDC issuers are global, so an unset audience lets any workflow obtain
Verified=true. provenance.New now refuses to start (Config.checkOIDCAudience)
when issuers are configured but PREPUB_OIDC_AUDIENCE is empty; cmd/prepub exits
non-zero. Covered by config_test.go; verified with go build/vet/test.
Remove the receiver-side fallback that derived its broker key from
PREPUB_HMAC_SECRET, and drop the vestigial receiver HMACSecret requirement (no
verification consumer) - a receiver now holds only its own PREPUB_NODE_KEY, so a
compromise can't derive other nodes' keys or mint publisher tokens. Add
`prepub node-key <node>` to provision a receiver's key on the publisher.
Clearer name for the per-node broker enrollment key read by cvmfs-prepub
receivers (Stratum-1 nodes). No behaviour change.
Replace FIFO bucket eviction with O(1) LRU (container/list): every request
refreshes its IP to most-recently-used, so a spoofed-IP flood evicts other
one-shot entries instead of active legitimate clients. Clamp maxIPs>=1. Add
ratelimit_test (LRU + token budget; -race and negative-control verified).
Resolve a symlinked spool (SELinux 226/NAMESPACE), make the CAS dir optional
in the unit, read cas/spool paths from config.yaml, exact group checks.
Decide 'mounted' from the mount table, not from /cvmfs/<repo> existing; ask
the stratum0 which parents are published and ingest the missing ones.
The directory-chain ingest crashed swissknife and at -b / its changes were
dropped by the receiver merge. Needs CVMFS_GW_MKDIR_PARENTS on the gateway.
Backoff 1-30 min within retry_window (24h); conflicts and bad payloads fail at once.

Payload deleted on any final state; attempts and last_error shown per job.
The old 'local publish mode' message also fired for the ingest
backend and misled operators.
bits no longer exports NO_FIELDS; compare the empty field-set digest.
ADR, MEASUREMENTS and review ids pointed at documents that are not part of
the repository; the design plans move out of the tree.
Drops warm quorum, /s1/catchup, /s1/{txn}/lease, GC pins and prepubctl.
The post-commit root-catalog pull now works and is hash-verified; one
--receiver-stratum0-url serves both paths; YAML false is honoured.
Checked against the code; removed features and duplicated sections dropped,
undocumented endpoints and flags added. Outdated diagrams removed.
Persisted revocation, stricter discovery and provenance checks, input
validation, recovery and shutdown fixes, CVMFS-compatible catalog flags
and hashes, role-aware install.sh; docs updated; Apache-2.0 headers throughout.
install.sh --prewarm/--no-prewarm sets prewarm in config.yaml.

Published-bytes counter for throughput in MB/s.
replace=true on the job; replace_on_conflict on the node only allows it. Replaced before the commit when the published hash is readable and differs; a failed commit never deletes. Health reports replace_allowed.
Records the wait for the per-repo commit lock. Keeps downloaded catalogs by hash (read-only, size-capped, LRU) in CacheDirectory or <spool>/catalog-cache.
Each ingest logs its output lines with when they arrived, so a slow
publish can be split into transaction, swissknife and close. Records
gain the pre-commit check and ancestors times.
install.sh --s3-conf-from writes cas.server_conf from the repository's
S3 config (keys refreshed on update, tuning kept); prepub passes it to
cvmfs_server ingest as --s3-config.
…tore SELinux labels

connect-gw runs once when the gateway key is in place (mountless unless
--mounted); the service user comes from CVMFS_USER when not given; the S3
source defaults to the file the copy names; restorecon on the installed paths.
…t writes under

prepub refuses to start when the S3 config names another alias than the store's.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants