Skip to content

fix(replication): stop gating capabilities on exact contract versions - #216

Merged
reatang merged 1 commit into
mainfrom
fix/runtime-capability-version-gate
Sep 1, 2026
Merged

fix(replication): stop gating capabilities on exact contract versions#216
reatang merged 1 commit into
mainfrom
fix/runtime-capability-version-gate

Conversation

@reatang

@reatang reatang commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Pull Request

Description

The bucket replication page renders "Replication capabilities are unavailable on this server." against current servers, and the Add / Edit / Delete rule controls are disabled — while replication itself keeps working and objects are replicated normally. The capability payload only gates console UI controls; it never reaches the replication engine.

Root cause: normalizeRuntimeCapabilities() rejected any capability block whose contract_version was not exactly 1. The server has since bumped the remote target contract to v4:

Server change Version
rustfs#6376 — per-target disableProxy v2
rustfs#6857 / rustfs#6860 — temporary target credentials v3
rustfs#6876 — temporary-credential fields promoted to writable v4

So remote_targets normalized to null, the whole snapshot was dropped, and the error alert was shown.

The contract version describes the shape of a capability block, which has been stable since v1. Semantics live in status and the self-describing fields[].state list, which both replication forms already read per field. This PR accepts any integer version >= 1 and keeps the fail-closed behaviour where it belongs:

  • missing, zero, negative, fractional or non-numeric contract_version → still rejected
  • status.state !== "supported" → every field of that feature is still treated as unsupported
  • unknown field name → still unsupported

The same exact-match gate on the storage_classes block is relaxed identically; it would have broken the same way on its next server-side bump.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test improvements
  • Security fix

Testing

The pre-existing "fail closed on unknown contract versions" case asserted the buggy behaviour, so it was rewritten into three cases: newer versions are accepted (using the real remote_targets v4 / storage_classes v2 shape), invalid versions still fail closed (undefined, 0, -1, 1.5, "1"), and a missing storage_classes block still fails closed.

Additionally verified end to end with a payload built from the server's actual v4 field lists (crates/replication/src/config.rs): capabilities normalize, both forms' requiredBucketFieldsSupported / requiredTargetFieldsSupported evaluate to true, and edge is still reported unsupported — i.e. controlsLocked becomes false and the rule controls are usable again.

  • Unit tests added/updated
  • Manual testing completed
node --experimental-strip-types --import ./scripts/register-typescript-loader.mjs --test tests/lib/*.test.{js,ts}   # 460 passed
tsc --noEmit          # clean
eslint                # clean
prettier --check .    # clean

Note: pnpm test:run could not be invoked through corepack in this environment (pinned pnpm 11.11.0 vs local 11.8.0), so the underlying script commands were run directly.

Checklist

  • Code follows the project's style guidelines
  • Self-review completed
  • TypeScript types are properly defined
  • All commit messages are in English (Conventional Commits)
  • All existing tests pass
  • No new dependencies added, or they are justified

Related Issues

N/A — reported through manual replication testing.

Screenshots (if applicable)

Before: the replication page showed a destructive alert titled "Replication rules unavailable" with the body "Replication capabilities are unavailable on this server.", while the site-replication rule was listed as Enabled and Add / Edit / Delete were greyed out.

Additional Notes

No server-side change is required. This also removes the recurring maintenance trap where every server capability bump silently disables console controls until the console ships a matching release.

The console rejected any runtime capability block whose contract_version
was not exactly 1. Servers have since bumped the remote target contract
to v4 (#6376 disableProxy, #6857/#6860 temporary credentials, #6876
promoting them to writable fields), so normalizeRuntimeCapabilities()
dropped the whole snapshot and the bucket replication page rendered
"Replication capabilities are unavailable on this server." while
replication itself kept working — the capability payload only gates the
UI controls, never the replication engine.

The contract version describes the *shape* of a capability block, which
has been stable since v1; semantics live in `status` and the
self-describing `fields` list, which the forms already read per field.
Accept any integer version >= 1 and keep failing closed on a missing,
zero, negative, fractional or non-numeric version, on an unsupported
capability status, and on unknown field names.

Also applies to the storage-class block, which had the same exact-match
gate and would have broken the same way on its next bump.
@reatang
reatang merged commit b6d1a18 into main Sep 1, 2026
10 checks passed
@reatang
reatang deleted the fix/runtime-capability-version-gate branch September 1, 2026 08:01
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.

1 participant