Skip to content

Releases: runcycles/cycles-server-admin

v0.1.25.51 — tenant webhook admin-category boundary (#209, deploy after events 0.1.25.23)

Choose a tag to compare

@amavashev amavashev released this 12 Jul 10:48

Security release — tenant-owned webhook subscriptions can no longer carry or receive admin-only event classes (issue #209). The admin-plane half of a coordinated three-layer fix (with cycles-server-events 0.1.25.23 and governance spec v0.1.25.40/.41). Enforces governance WEBHOOK SUBSCRIPTION INVARIANT 2: a subscription owned by a concrete tenant (tenant_id present and != "__system__") must not carry or be delivered admin-only selectors/events (api_key.* / policy.* / webhook.* / system.*).

Defense in depth — three layers:

  • Write-path gate: the admin webhook create/update paths now validate event_categories (not just event_types) against the tenant-accessible boundary for concrete-tenant targets — closing the injection vector where a tenant could smuggle admin-only categories, and the admin-on-behalf-of / admin-plane path that could place them on a tenant-owned row. __system__ targets are exempt. Shared WebhookCategoryBoundaryValidator so the two planes can't drift.
  • Dispatch boundary (the durable guarantee): live dispatch AND replay skip, per event, any admin-only event to a concrete-tenant subscription — fail-closed on type OR category OR unclassifiable record, independent of reconciler state or stored-selector correctness. A mixed subscription still receives its tenant-accessible events. (The last-mile enforcement at HTTP-send time is in cycles-server-events 0.1.25.23.)
  • Startup reconciler: SSCAN-batched, atomic CAS writes, background daemon with retry — strips admin-only selectors from existing concrete-tenant offender rows (disables empty-both rows), and migrates null-owner rows to __system__. A reconcile-dry-run mode reports without mutating.

Also: the empty-both webhook match-ALL update door is closed; the tenant/admin webhook boundary is normative and conformance-binding (INVARIANT 2). Replay hardening: correct pagination (no equal-timestamp skips, no duplicates), all-or-narrow completeness (a replay delivers every deliverable event in its window or returns 400 to narrow — never a silent partial), max_events validated 1–1000, and structured dispatch classification (a real backend failure logs a degraded warning; an intended concurrent-disable/guard logs benign info). /test synthetic probe is a documented, narrow invariant exception.

⚠ Deploy ordering (operational, important): deploy and fully roll out cycles-server-events 0.1.25.23 FIRST (the last-mile boundary) before this version. This admin version begins blocking admin-only selectors at the write/enqueue path; the events worker enforces the boundary at HTTP send for anything already queued or retried. If admin is deployed before the events fleet is fully upgraded, a mixed-version window exists where an old (0.1.25.22) worker can still send a queued violating delivery. The full-stack compose in this release pins events 0.1.25.23.

Operators — audit/remediate existing rows: the reconciler auto-strips offenders on startup (dry-run available); the 0.1.25.50/.51 CHANGELOG includes a redis-cli/jq audit recipe. Migration for legit per-tenant admin monitoring: use a __system__-owned subscription with client-side tenant filtering on the envelope tenant_id (admin lifecycle events are null-scoped, so a scope_filter cannot select them per tenant; only policy.* is scope-filterable). Residual provenance/ownership follow-up tracked as #209's linked issue.

Compatibility: BEHAVIOR CHANGE — admin create/update requests provisioning admin-only categories/types on a concrete-tenant subscription now return 400 (previously 201/200); replay windows exceeding the scan limit or max_events deliverable count now return 400 (narrow the window). No Redis/wire-schema change. Governance spec v0.1.25.40/.41 (runcycles/cycles-protocol). Version bump 0.1.25.50 → 0.1.25.51.

1,600+ tests green; JaCoCo ≥95% on all modules; external security review (codex) SHIP across the full fix. Full details: CHANGELOG.md [0.1.25.51], AUDIT.md, PR #210.

v0.1.25.50 — security: tenant webhook event_categories boundary

Choose a tag to compare

@amavashev amavashev released this 11 Jul 00:05

Security release. Closes an authorization gap in tenant self-service webhook subscriptions.

Security

  • Tenant-plane webhook event_categories are now validated against the tenant-accessible boundary. Tenant self-service webhook create and update rejected admin-only event_types (api_key/policy/webhook/system), but did not validate event_categories — and category matching is additive with type matching. A tenant key could create or update a subscription with one allowed event_type plus "event_categories": ["api_key"] (or policy/webhook/system) and receive admin-only event classes for its tenant. Both create and update now reject admin-only categories with 400 INVALID_REQUEST, deriving the boundary from a single EventCategory.isTenantAccessible() source (governance spec revision v0.1.25.38, runcycles/cycles-protocol#126). Admin-plane webhook endpoints are unaffected.
  • Operators: audit existing tenant-plane subscriptions created on 0.1.25.49 and earlier for smuggled admin-only categories, and for the match-ALL rows below. A copy-pasteable redis-cli + jq recipe (flagging both ADMIN_CATEGORIES and MATCH_ALL rows, with the admin-provisioned-provenance caveat) is in CHANGELOG.md [0.1.25.50] Security.

Fixed

  • Empty-both match-ALL door closed. WebhookService.update applied an empty event_types: [] verbatim; with no categories, the matcher's empty-both branch made the subscription match every event class (admin-only included). Update now rejects a change that would leave a subscription with neither event_types nor event_categories (both planes; the state is not creatable on either). Category-only subscriptions remain legal.

Follow-up (tracked, not in this release): whether a tenant should retain admin-only categories on a legacy/admin-created subscription it owns is a separate provenance/ownership question — tracked as #209.

Compatibility

  • No HTTP request/response schema, Redis data model, or Lua change. Requests that previously smuggled admin-only categories now return 400; legitimate tenant-accessible categories are unaffected. Legacy offender rows keep delivering until repaired — see the audit recipe.
  • Full-stack compose bundles cycles-server 0.1.25.47 and cycles-server-events 0.1.25.22.

1,566 tests green including integration + contract validation against governance spec v0.1.25.38; JaCoCo ≥95% on all modules; external security review (codex) SHIP. Full details: CHANGELOG.md [0.1.25.50], AUDIT.md, PR #208.

v0.1.25.49 — spec-conformant webhook scope-filter matching + replay filtering

Choose a tag to compare

@amavashev amavashev released this 10 Jul 19:02

Spec-conformance release: webhook scope_filter matching now implements the admin OpenAPI spec's wildcard semantics, and replay honors the subscription's scope filter.

Changed

  • BEHAVIOR CHANGE — webhook scope_filter matching is spec-conformant. WebhookRepository.matchesScope previously did literal prefix matching (trailing-/* filters matched nothing, any startsWith scope matched, null-scope events matched every filter). Now per the spec: exact match without a wildcard; trailing * = prefix match on children only (non-empty child segment; the bare base scope and the degenerate base/ do not match); bare * = any event that has a non-blank scope; null/blank event scopes are unscoped and excluded from every scope-filtered subscription; null/blank filters still match everything. See CHANGELOG [0.1.25.49] for the full migration guide (descendants-only rewrites, base+descendants needing two subscriptions, accidental sibling-prefix matches, unscoped events).
  • With cycles-server v0.1.25.47's matching refinements, both planes now match identically — the same filter delivers the same scopes whether an event is emitted by the runtime or the admin plane, and both matchers pin the same test table.

Fixed

  • Webhook replay honors scope_filter. Replay previously queued every event in the requested range (after event_types filtering) directly to the subscription, bypassing scope matching — it could deliver events the subscription would never have received live. Replay now applies the same spec-conformant matcher as live dispatch.

Compatibility

  • No HTTP request/response schema, Redis data model, or Lua change. Which subscriptions receive a given event (live or replayed) can change as described above — operators using bare-prefix filters should apply the CHANGELOG migration guide.
  • Release prep pins: full-stack compose bundles cycles-server 0.1.25.47 and cycles-server-events 0.1.25.22.

Full details: CHANGELOG.md [0.1.25.49], AUDIT.md entries, PR #206 (11+6 matcher tests + 5 replay tests ported to both planes).

v0.1.25.48 — EventDataTenantCascade payload mapping + fleet pin alignment

Choose a tag to compare

@amavashev amavashev released this 04 Jul 15:39
ed921b9

Internal-consistency release: typed cascade event payload mapping + fleet pin alignment. No wire change.

Fixed

  • Cascade event payloads map to their own typed class. The four *_via_tenant_cascade EventTypes were registered in EventPayloadTypeMapping against nearest-fit lifecycle classes; the payload TenantCloseCascadeService actually emits fits none of them, so the warn-only payload-shape validator flagged every cascade emission. New EventDataTenantCascade model mirrors the spec schema of the same name (governance spec v0.1.25.35); all four kinds remap to it. Event.data on the wire is unchanged — only the internal validation/registry class moved.

Changed

  • Prod compose self-pin → 0.1.25.48; bundled full-stack pins → cycles-server 0.1.25.46 (public-endpoint 429 rate limiting) and cycles-server-events 0.1.25.22 (delivery-time SSRF guard — webhook targets on http/private networks need allow_http/CIDR config via PUT /v1/admin/config/webhook-security; see that release's notes).

Compatibility
No HTTP request/response, Redis, event-wire, or spec-surface change.

Full details: CHANGELOG.md [0.1.25.48], AUDIT.md 2026-07-04 entries. 804 tests green, spec coverage 46/46, JaCoCo ≥95%.

v0.1.25.47 — production deployment hardening: image healthcheck, JAVA_OPTS, full-stack readiness + fleet pins

Choose a tag to compare

@amavashev amavashev released this 26 Jun 17:38
16b9d44

[0.1.25.47] — 2026-06-26

Fixed

  • Production Compose image tags now point at cycles-server-admin:0.1.25.47.
  • The admin runtime image now includes a built-in readiness healthcheck and
    starts Java through exec java $JAVA_OPTS -jar app.jar, so the JVM receives
    container SIGTERM directly and operators can tune JVM flags through
    JAVA_OPTS.
  • Production Compose now sets conservative JVM options for the admin container
    through JAVA_OPTS.
  • Full-stack production Compose now references current published sibling images:
    cycles-server:0.1.25.44 and cycles-server-events:0.1.25.20.
  • Full-stack production Compose now passes ADMIN_API_KEY to the runtime
    service, disables runtime public SpringDoc/Swagger and tenant-labelled custom
    Prometheus metrics, and probes runtime readiness rather than aggregate health.
  • Full-stack production Compose now publishes the events management port 9980
    and probes /actuator/health/readiness; the worker app port 7980 remains
    internal.
  • README/OPERATIONS deployment docs now reflect required production Compose
    environment variables, current Spring Boot/Jedis versions, protected docs
    defaults, JAVA_OPTS, and the events management-port split.

Compatibility

  • Deployment and documentation hardening only. No admin HTTP schema, Redis data
    model, event payload, webhook wire, Lua, or cycles-protocol spec change.

v0.1.25.46 — dependency alignment: Jedis 7.5.2 + springdoc 2.8.17

Choose a tag to compare

@amavashev amavashev released this 26 Jun 13:13
20fcb45

[0.1.25.46] — 2026-06-26

Changed

  • Jedis 7.5.0 → 7.5.2 — aligns the admin service with cycles-server (already
    on 7.5.2) and picks up the latest 7.5.x Redis-client patches.
  • springdoc-openapi 2.8.16 → 2.8.17 — latest 2.8.x, matching cycles-server.

Compatibility

  • Dependency-currency only. No HTTP request/response, Redis data model, Lua,
    event, webhook-wire, or spec change.

v0.1.25.45 — production readiness: operational-endpoint auth, Redis health, webhook-secret encryption enforcement

Choose a tag to compare

@amavashev amavashev released this 25 Jun 16:17
adc3b57

[0.1.25.45] — 2026-06-25

Security

  • Hardened production Compose Redis defaults: Redis no longer publishes
    6379, requires REDIS_PASSWORD, authenticates health checks, and restarts
    unless stopped.
  • Protected aggregate actuator, OpenAPI, and Swagger endpoints with
    X-Admin-API-Key; exact liveness and readiness probe endpoints remain open.
  • Production Compose now requires WEBHOOK_SECRET_ENCRYPTION_KEY and starts
    the admin service with WEBHOOK_SECRET_ENCRYPTION_REQUIRED=true, causing
    startup to fail closed when webhook secret encryption is required but no key
    is configured.
  • Webhook test delivery now re-validates stored target URLs against the
    current SSRF policy before opening an outbound connection.

Changed

  • Production health checks and release smoke tests now use
    /actuator/health/readiness, which includes Redis readiness.
  • Generated API docs are disabled by default unless API_DOCS_ENABLED=true.
  • Production Compose enables per-source, per-process throttling for repeated
    401/403 authentication failures; throttled responses return
    429 LIMIT_EXCEEDED without writing another failure audit row.
  • Production Compose image tags now point at 0.1.25.45.

Compatibility

  • No admin request/response body, Redis key/value, Lua, event payload, webhook
    payload, or cycles-protocol spec change.
  • Operational tooling that reads protected actuator or docs endpoints must send
    X-Admin-API-Key. Kubernetes-style liveness/readiness probes do not need an
    admin key.

v0.1.25.44 — operational logging context + log sanitization (rolls up .43)

Choose a tag to compare

@amavashev amavashev released this 25 Jun 09:49
fdfee7f

[0.1.25.44] — 2026-06-24

Fixed

  • Added common CR/LF flattening for dynamic operator-log fields across
    exception, auth rejection, controller side-effect, bulk-row, event dispatch,
    webhook test/replay, webhook security config, and audit fallback logs.
  • Extended the same flattening to the data-plane repository, idempotency, and
    config logs (api-key, audit, budget, event, policy, tenant, webhook,
    webhook-delivery) via a shared LogSanitizer, so request-derived strings
    logged below the controller layer cannot inject log lines either.
  • Flattened caller-supplied X-Request-Id at filter ingress and sanitized
    request-attribute helper methods before those values are reused in auth,
    exception, audit, and controller logs.
  • Kept the v0.1.25.43 operational context while preventing request/config/
    exception strings from injecting misleading log lines.

Compatibility

  • No admin HTTP request/response, Redis key/value, Lua, event payload, webhook
    wire, or spec change.

[0.1.25.43] — 2026-06-24

Changed

  • Ops logging context review. Admin API exception, auth rejection,
    controller side-effect, webhook replay/test, event dispatch, idempotency,
    audit, and Redis repository logs now include stable operational identifiers
    such as method/path/route, status/error code, tenant, key id, budget/policy/
    webhook/event/delivery ids, request id, trace id, correlation id, queue names,
    and exception class where available.
  • Replaced the ambiguous Landed in governance exception handler log with a
    structured Governance exception handled record that names the endpoint,
    error code, status, request id, trace id, and exception class.
  • Startup, CORS, Redis, and webhook security config logs now use structured
    fields instead of banner-style or context-poor text.
  • Sensitive or noisy values are avoided in logs: API-key/idempotency secrets are
    not emitted, idempotency keys are fingerprinted, and webhook test delivery
    failures log target_host instead of the full subscriber URL.

Compatibility

  • No admin HTTP request/response shape change.
  • No Redis key, queue, Lua, or payload-shape change.
  • No cycles-protocol spec change.

v0.1.25.42 — Apache Tomcat CVE patch (rolls up Jedis 7.5.0 + SB 3.5.15 + CI/supply-chain hardening)

Choose a tag to compare

@amavashev amavashev released this 24 Jun 16:23
c089901

Security and dependency-hygiene rollup accumulated since v0.1.25.41. No code
or wire-format changes — pom, CI, and ops only. The headline is the Apache
Tomcat CVE patch; the same release boundary also rolls up a Jedis major bump,
a Spring Boot patch, and supply-chain/CI hardening.

Changed

  • Tomcat 10.1.54 → 10.1.55 (security). Re-introduced the
    <tomcat.version>10.1.55</tomcat.version> override to close 3 CRITICAL +
    3 HIGH + 1 LOW tomcat-embed-core CVEs (CVE-2026-43515 / -43512 / -41293
    CRITICAL; -43513 / -42498 / -41284 HIGH; -43514 LOW) that Trivy flagged
    against Spring Boot 3.5.14's managed 10.1.54. Same override pattern as the
    retained commons-lang3 pin; remove once Spring Boot's BOM manages
    10.1.55+. Full CVE breakdown in AUDIT.md.
  • Jedis 6.2.0 → 7.5.0 (major). Redis-client major bump aligning the
    admin service with the rest of the fleet. All call sites use stable APIs;
    CI's full suite (782+ tests) passes on 7.5.0.
  • Spring Boot 3.5.14 → 3.5.15 (patch). Upstream patch release. The
    commons-lang3 3.18.0 override is retained — SB 3.5.15's BOM still manages
    3.17.0, where CVE-2025-48924 is unfixed.
  • Container log rotation. All four docker-compose*.yml files gain a
    shared json-file logging anchor (max-size: 10m, max-file: 5), capping
    each container at 50 MB to prevent unbounded *-json.log growth on
    long-running deployments.

Security / CI

  • Supply-chain hardening: OpenSSF Scorecard workflow, all third-party GitHub
    Action SHAs pinned, workflow token permissions tightened, Alpine gnutls
    CVE-2026-33845 patched in the image, and a two-phase Trivy-gated release
    build (scan before push).
  • Trivy container gates (release + PR) now set limit-severities-for-sarif: true so exit-code honors the declared HIGH,CRITICAL severity. In
    format: sarif mode the action otherwise gates on any fixable finding of
    any severity. A known fixable MEDIUM (jackson-databind CVE-2026-54515,
    fix awaiting the unreleased jackson 2.21.5) is reported but no longer
    blocks publish; HIGH/CRITICAL still fail the gate.

v0.1.25.41 — drop tomcat override (SB 3.5.14 BOM-managed) + Jedis fleet alignment

Choose a tag to compare

@amavashev amavashev released this 26 Apr 13:31
7fec471

Dependency hygiene release. No application-level code or wire-format changes — pom-only patch.

Changed

  • Spring Boot 3.5.13 → 3.5.14. Patch upgrade picking up upstream security hardening (constant-time DevTools secret comparison, RandomValuePropertySource SecureRandom, consistent SSL hostname verification, ApplicationPidFileWriter/ApplicationTemp symlink fixes).
  • Drop <tomcat.version>10.1.54</tomcat.version> override. SB 3.5.14's BOM now manages Tomcat 10.1.54 directly (verified against spring-boot-dependencies-3.5.14.pom). The explicit pin from v0.1.25.33 (closing CVE-2026-34483 / CVE-2026-34487) is now redundant.
  • Jedis 5.2.0 → 6.2.0 (major). Aligns with cycles-server-events (6.2.0) and cycles-server (6.2.0) on a single Redis-client major across the fleet. Jedis 6.1.0 explicitly restored binary compatibility for SetParams (#4225 upstream); all 782 tests pass on 6.2.0.
  • commons-lang3 3.18.0 override retained — SB 3.5.14's BOM still manages 3.17.0 (CVE-2025-48924 unfixed there). Comment updated to reference SB 3.5.14.

See CHANGELOG.md for the full entry.

Fleet alignment

Matching releases: cycles-server-events v0.1.25.12, cycles-server v0.1.25.18.