Skip to content

feat(config): federated configuration sharing — engine + type registry + Flows (+ e2e)#2134

Merged
rubenvdlinde merged 1 commit into
developmentfrom
feat/federated-config-sharing
Jul 26, 2026
Merged

feat(config): federated configuration sharing — engine + type registry + Flows (+ e2e)#2134
rubenvdlinde merged 1 commit into
developmentfrom
feat/federated-config-sharing

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Builds the foundation of the fleet-wide federated configuration sharing standard (design carried in the openspec change; supersedes the design-only #2132). One OpenRegister-owned way for any app to share configuration over GitHub — Flows is the first consumer (the reframed #2065).

What

  • IShareableConfigType + RegisterShareableConfigTypesEvent + registry — the contribution seam, same idiom as flow nodes. Storage-agnostic: a type owns (de)serialisation, so app-specific storage (e.g. NL Design themes in IConfig) works too, not only OR objects.
  • FederatedConfigService — the engine: types(), bundle() (serialise a selection), install() (deserialise, gated by the org source allowlist), publish() (push to GitHub with the token supplied by the credential broker → Doriath's vault where installed, NC vault otherwise; the token never enters the service). Trust v1 = org allowlist (empty = not-yet-enforced) + version pinning + secret exclusion.
  • FlowShareableConfigType — flows as a type: serialise to a portable shape (no id/uuid/owner/organisation), install back into the flow store.
  • FederatedConfigController + routes (/api/federated-config/types|bundle|install), all #[NoCSRFRequired].

Verification

  • Unit (13): registry dispatches once, service allowlist enforcement + org-prefix match, Flow type round-trip strips instance fields. phpcs clean.
  • Playwright e2efederated-config.spec.ts (4): types include Flows; a flow bundles → installs as a fresh flow → the installed flow RUNS to completion; unknown type → 404; the org allowlist refuses a non-allowlisted source (403) and allows an allowlisted one.
  • Live (8080): full round-trip — bundle a flow → install a fresh one → run it → {shared:true}.

Design decisions baked in (from review)

OR service + type registry · GitHub credentials via Doriath (broker vault leaf) · trust v1 = org allowlist + pinning · sharing user/org-scoped · #2065 collapses to one flow-shaped consumer · no openconnector dependency.

Follow-ups (design phases 4–6)

Fold hermiq's duplicated GitHub sync onto this · roll out declared types per app (procest case types, shillinq payroll packs, opencatalogi publication types, docudesk, softwarecatalog, pipelinq, larpingapp, NL Design themes) · publish/discover UI · per-org RBAC beyond admin · signing.

🤖 Generated with Claude Code

…y + Flows (+ e2e)

Builds the foundation of the fleet-wide federated-config standard (design in the
carried openspec change; supersedes the design-only #2132). One OpenRegister-
owned way for any app to share configuration over GitHub; Flows is the first
consumer (the reframed #2065).

- IShareableConfigType + RegisterShareableConfigTypesEvent + registry — the
  contribution seam, same idiom as flow nodes. Storage-agnostic: a type owns
  (de)serialisation, so app-specific storage (e.g. NL Design themes in IConfig)
  works too, not only OR objects.
- FederatedConfigService — the engine: types(), bundle() (serialise a selection),
  install() (deserialise, gated by the org source allowlist), publish() (push to
  GitHub with the token supplied by the credential broker — Doriath's vault where
  installed, NC vault otherwise; the token never enters the service). Trust v1 =
  org allowlist (empty = not-yet-enforced) + version pinning + secret exclusion.
- FlowShareableConfigType — flows as a type: serialise to a portable shape (no
  id/uuid/owner/organisation), install back into the flow store.
- FederatedConfigController + routes (/api/federated-config/types|bundle|install),
  all #[NoCSRFRequired] so API/MCP clients can call them.

Tests: 13 unit (registry event-once, service allowlist enforcement + prefix
match, Flow type round-trip strips instance fields). Playwright e2e
tests/e2e/api-direct/federated-config.spec.ts (4): types include Flows; a flow
bundles → installs as a fresh flow → the installed flow RUNS to completion;
unknown type = 404; the org allowlist refuses a non-allowlisted source (403) and
allows an allowlisted one. phpcs clean. Live-verified on 8080: full round-trip
(bundle a flow → install a fresh one → run it → {shared:true}).

Follow-ups (per the design's phases 4-6): fold hermiq's duplicated GitHub sync
onto this; roll out declared types per app (procest case types, shillinq payroll
packs, opencatalogi publication types, docudesk, softwarecatalog, pipelinq,
larpingapp, NL Design themes); the publish/discover UI; per-org RBAC beyond
admin; signing.
@rubenvdlinde
rubenvdlinde merged commit eab080c into development Jul 26, 2026
15 of 17 checks passed
@rubenvdlinde
rubenvdlinde deleted the feat/federated-config-sharing branch July 26, 2026 08:05
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 8b32ae2

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 174/174
npm ✅ 555/555
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-07-26 08:07 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde added a commit that referenced this pull request Jul 26, 2026
…iscovery, signing, per-org RBAC (#2147)

Builds out the store surface on top of the sharing engine (#2134/#2135/#2140):

- **Publish endpoint** `POST /api/federated-config/publish` — publishes a signed
  bundle to GitHub using the credential the USER selected (read from the
  `federated-config-credential` preference), never one passed by the caller.
  Refuses (400) when no store credential is selected.
- **Discovery** `GET /api/federated-config/discover?topic=` — searches GitHub by a
  type's discovery topic (anonymous, or through the broker when the user has a
  credential). Backs a browse UI; never installs.
- **Signing / verification** (`BundleSigner`, Ed25519 via libsodium) — publish
  signs the canonical bundle with the instance key (minted + stored once);
  install verifies. A tampered signature is always refused; unsigned/untrusted
  bundles are refused once the org populates a trusted-keys allowlist (empty =
  not-yet-enforced, the source-allowlist idiom). `GET /api/federated-config/
  public-key` exposes the instance key for others to trust.
- **Per-org RBAC** (`FederatedConfigAccess`) — publish/install gated by app-config
  group allowlists (`federated_config_publish_groups` / `_install_groups`); empty
  = any signed-in user, admins always allowed.

15 unit (BundleSigner round-trip/tamper/canonicalisation/enforcement; RBAC
membership; install signing gate) + 6 Playwright e2e. Live-verified on 8080:
public-key served; discover returned real GitHub repos; signing enforcement 403→
cleared 200 (after appconfig-cache restart); RBAC 403 for a non-admin gated user,
200 for admin; publish 400 with no credential selected.

@SPEC openspec/changes/federated-config-sharing/specs/federated-config-sharing/spec.md
rubenvdlinde added a commit that referenced this pull request Jul 26, 2026
…tandard (#2154)

The change captured only the initial design (5 requirements, most tasks open). It
now reflects everything shipped and merged across the fleet:

- 6 new requirements — schema-marker mechanism (REQ-FCS-006), Ed25519 signing +
  verification (007), per-org group RBAC (008), config sets (009), repo-creation +
  topic-tagging + fetch bridge (010), and installable config-set repositories /
  OpenBuild convergence (011). Each with scenarios; `openspec validate --strict`
  passes.
- tasks.md updated to the shipped reality with PR references across openregister
  (#2134/#2135/#2140/#2147/#2148/#2149), nldesign #192, the 8 app markers, hermiq
  #126/#127, openbuild #181, nc-vue #242 — plus the real-GitHub end-to-end
  verification and the two external follow-ups.
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