Skip to content

docs: design document for Updates - #242

Open
paullatzelsperger wants to merge 2 commits into
mainfrom
docs/design_update_activity
Open

paullatzelsperger wants to merge 2 commits into
mainfrom
docs/design_update_activity

Conversation

@paullatzelsperger

Copy link
Copy Markdown
Contributor

This PR adds a design document for Update activities

@paullatzelsperger paullatzelsperger self-assigned this Nov 13, 2026
@paullatzelsperger paullatzelsperger added documentation Improvements or additions to documentation Design labels Nov 13, 2026
@jimmarino

Copy link
Copy Markdown
Contributor

Thanks for putting this together. It gives us a good base. The distinct orchestration type, the PATCH operation, the update field on the profile, and ProcessUpdate all look right to me. I have been thinking about two goals for updates, and I would like to suggest some adjustments that I think get us there.

Two goals we should aim for:

  1. An agent written outside the CFM repository can handle updates without a change to the Tenant Manager.
  2. An update adds as little special-case code as possible. It uses the mechanisms CFM already has: the orchestration template and dependsOn that deploy and dispose use, and the diff format of the tenant PATCH.

Adjustments

  • The Tenant Manager sends the stored and the requested profile data to the agents, and each agent determines its own changes (goal 1).
  • The template has an activity list for the update type, so provisionManager.Start and ActivityDefinition stay as they are (goal 2).
  • The update activities are ordered with dependsOn, so that the holder record is written before a credential is requested.
  • The request body uses the tenant diff, so that a client cannot remove data by leaving it out.
  • Each connector config entry is its own property, so that the diff can set or remove a single entry.
  • The default Process stays as it is today.
  • Output values of a successful update are merged into cfm.vpa.state, so that an agent can find what it wrote earlier.
  • A failed update is followed by one compensation run, so that the client does not have to repair it.

The sections below describe how this would work. If you see a problem with any of it, let's discuss here or on a call.

Flow

  1. Request. The client sends PATCH on the participant profile, in the tenant diff format (see Request format).
  2. Manifest. The Tenant Manager applies the request to the stored profile data to produce "after." It sets the listed values and deletes the listed keys, and computes nothing else. It then sends a manifest of type cfm.orchestration.vpa.update. The payload contains:
    • the deploy keys (cfm.vpa.data, cfm.vpa.credentials) with the requested data ("after")
    • one key with the data stored on the profile ("before"), in the same format
    • participantContextId and cfm.vpa.state
  3. Definition. The Provision Manager loads the stored update definition, as it does for deploy and dispose (see Update orchestration type in the template).
  4. Execution. Every activity in the definition runs on every update, in the defined order. Each agent:
    • reads its part of "before" and "after"
    • completes immediately if they are equal
    • otherwise reads the current state from its downstream system
    • writes what "after" requires and the downstream system lacks
    • removes what is in "before," absent from "after," and present downstream
    • sets an output value for each downstream write, including on the error path
  5. Success. The Tenant Manager replaces the stored profile data with "after" and merges the update's output values into cfm.vpa.state.
  6. Failure. The Tenant Manager sends a second update manifest with "before" and "after" exchanged, and includes the output values of the failed run. The agents run the same code. If this second run also fails, the Tenant Manager marks the update as failed and starts nothing more. The operator retries the update or corrects the downstream systems manually.

Update orchestration type in the template

An orchestration template maps each orchestration type to a list of activities. The update type is a third key, cfm.orchestration.vpa.update, next to deploy and dispose. The format of the entries does not change.

{
  "id": "example-template",
  "description": "Participant deployment, disposal, and update",
  "activities": {
    "cfm.orchestration.vpa.deploy": [
      { "id": "register",  "type": "registration-activity", "dependsOn": [] },
      { "id": "connector", "type": "edcv-activity",         "dependsOn": [] },
      { "id": "onboard",   "type": "onboarding-activity",   "dependsOn": ["register"] }
    ],
    "cfm.orchestration.vpa.dispose": [
      { "id": "offboard",          "type": "onboarding-activity",   "dependsOn": [] },
      { "id": "connector-cleanup", "type": "edcv-activity",         "dependsOn": [] },
      { "id": "deregister",        "type": "registration-activity", "dependsOn": ["offboard"] }
    ],
    "cfm.orchestration.vpa.update": [
      { "id": "holder-update",      "type": "registration-activity", "dependsOn": [] },
      { "id": "connector-update",   "type": "edcv-activity",         "dependsOn": [] },
      { "id": "credentials-update", "type": "onboarding-activity",   "dependsOn": ["holder-update"] }
    ]
  }
}
  • The deploy and dispose lists are shortened for this example. A real template lists more activities.
  • The update list contains only the agents that support change. Agents that do not support change are not listed and are not called.
  • credentials-update depends on holder-update, so the holder record is written before a credential is requested.
  • Each activity in the update list receives the discriminator cfm.orchestration.vpa.update, and the executor calls ProcessUpdate on its agent.
  • A template without the cfm.orchestration.vpa.update key does not support changes.

Request format

Every map uses the existing tenant diff (TenantPropertiesDiff): properties holds the keys to set, and removed holds the keys to delete. The Tenant Manager sets first and deletes second, as PatchTenant does. A list uses added and removed. A part the client does not mention is unchanged.

{
  "properties": { "contact": "ops@acme.example" },
  "removed":    ["oldKey"],

  "participantRoles": {
    "properties": { "profile-1": ["supplier", "manufacturer"] },
    "removed":    ["profile-2"]
  },

  "vpaProperties": {
    "cfm.issuer": {
      "properties": { "legalName": "ACME Corp" },
      "removed":    ["formerKey"]
    },
    "cfm.connector": {
      "properties": { "config.edc.example.setting": "value" },
      "removed":    ["config.edc.old.setting"]
    }
  },

  "dataspaceProfileIds": {
    "added":   ["profile-3"],
    "removed": ["profile-2"]
  }
}
  • Top level. properties and removed apply to the profile's own properties. A request that changes only those has the same shape as a tenant PATCH, and the Tenant Manager stores it without sending a manifest.
  • participantRoles. One diff. The keys are dataspace profile IDs, and each value is the complete role list for that dataspace profile.
  • vpaProperties. One diff per VPA type. The VPA type selects the VPA and is not itself set or removed.
  • One level. Each diff sets or removes keys of one map. A value is replaced as a whole.
  • Connector config entries. Each config entry is its own property of the cfm.connector VPA, with the key prefix config. followed by the entry name. The diff therefore sets or removes single entries. The prefix name is a suggestion.

Rules

  1. A template may omit the update activity list. That template does not support changes. Start rejects the manifest, and the Tenant Manager marks the update as failed with the cause "updates not supported" and starts no compensation run.
  2. Credential specs of a dataspace profile do not change after creation. A different credential set is a new dataspace profile.
  3. Adding or removing a VPA is a non-goal, and it is handled by migration to a different orchestration template.
  4. After a failed update, entries the failed attempt wrote are removed when the agent records them. Otherwise, manual intervention may be needed.
  5. An agent completes without action only when every value it reads from the profile data is the same in “before” and “after.” For example, the edcv agent reads the connector config entries and the dataspace profile associations, so it compares both.
  6. A dispose is rejected while an update or its compensation run is pending, as it is while a deploy is pending.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Design documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants