Skip to content

Integrate MIAF and WFM identity profile SUPs - #194

Open
matlec wants to merge 44 commits into
pre-draftfrom
feat/miaf-integration
Open

Integrate MIAF and WFM identity profile SUPs#194
matlec wants to merge 44 commits into
pre-draftfrom
feat/miaf-integration

Conversation

@matlec

@matlec matlec commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Description

Integrates the two voted MIAF SUPs into the normative specification:

What changes

  • Adds an Identity and Authorization section: the framework, the X.509-SVID profile, TLS/mTLS requirements, the trust bundle and discovery endpoints (with OpenAPI), the identity lifecycle and operator playbooks, the WFM Identity Profile, and security considerations.
  • Reworks the Management Interface security model. mTLS with X.509-SVIDs replaces server-side TLS plus RFC 9421 payload signatures. The onboarding and certificate-download endpoints and the PayloadSignature scheme are removed.
  • Drops {clientId} from every route. Caller identity now comes from the authenticated SPIFFE ID. Retained endpoints gain a 403 for local-policy denial and Cache-Control: private.
  • Bumps the Workload Management OpenAPI to 3.1.0, which the mutualTLS scheme and nullable typing require.

Deliberate deviations from the SUPs

  • Cryptography is broader and built for agility. ECDSA P-256 is the only mandatory-to-implement algorithm; ECDSA P-384 and Ed25519 are optional; RSA PKCS#1 v1.5 is allowed only for certificate and CSR signatures, for CA compatibility. The SUP fixed ECDSA P-256 plus RSA-PSS and banned PKCS#1 v1.5.
  • TLS is pinned to RFC 9852 (TLS 1.3 default, TLS 1.2 only as a non-default fallback), and the spec spells out that the TLS 1.2 fallback exposes the client SPIFFE ID in the cleartext handshake. The SUP only required TLS 1.3 support.
  • A WFM Client derives the WFM it expects from its own SVID path rather than from a separately configured target wfm-id, so there is no separate target-identifier config item (which simplifies configuration and operations).
  • Adds hardening the SUPs left open: a trust-anchor rotation playbook, Bundle Map rollback protection via spiffe_sequence with fail-closed on an empty bundle, refresh driven by spiffe_refresh_hint, and RFC 9440 section 4 spoof-proofing for the forwarded Client-Cert header.
  • Expands the security-considerations catalog past the SUP tables: MIS compromise, central key-custody concentration, Bundle substitution and rollback, an unreliable validator clock, TLS 1.2 handshake disclosure, forwarded-header injection, and profile-specific threats.

Issues Addressed

Change Type

Please select the relevant options:

  • Fix (change that resolves an issue)
  • New enhancement (change that adds specification content)
  • Content edits (change that edits existing content)

Checklist

  • I have read the CONTRIBUTING document.
  • My changes adhere to the established patterns, and best practices.

Replace the Management Interface security model with
the MIAF and WFM Identity Profile SUPs: add the Identity
and Authorization section, switch to mTLS with X.509-SVIDs,
drop {clientId} from routes, and remove the onboarding,
certificate, and RFC 9421 payload-signing paths.

Signed-off-by: Matthias Lechner <[email protected]>
@phil-abb
phil-abb self-requested a review July 21, 2026 10:42
@ajcraig
ajcraig self-requested a review July 22, 2026 12:53

@phil-abb phil-abb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's some preliminary feedback.

Comment thread mkdocs.yml
Comment thread src/specification/margo-management-interface/resources/index.md.jinja2 Outdated
Comment thread src/specification/margo-management-interface/resources/index.md.jinja2 Outdated
Comment thread system-design/specification/identity/trust-bundle-api-swagger.md
Comment thread system-design/specification/identity/wfm-identity-profile.md Outdated
Comment thread system-design/specification/identity/wfm-identity-profile.md
Comment thread system-design/specification/identity/wfm-identity-profile.md Outdated
matlec and others added 12 commits July 23, 2026 10:31
Rework the Trust Bundle retrieval endpoint to return a single
SPIFFE bundle (a JWK Set) per the SPIFFE Federation bundle-
endpoint model, instead of a SPIFFE Bundle Map.

Scope federation as future work and state that a verifier
validates SVIDs against its own Trust Domain.

Signed-off-by: Matthias Lechner <[email protected]>
…uirements-and-security.md

Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
…rations.md

Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
…rations.md

Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
…rations.md

Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
Co-authored-by: Philip Presson <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
matlec added 2 commits July 24, 2026 07:52
Prefix every link to a page in the same folder
with "./" to force the Fumadocs resolving the
link as a relative link (solves the 404 issue)

Signed-off-by: Matthias Lechner <[email protected]>
The published site (Fumadocs, at docs.margo.org) builds its sidebar
from a meta.json in each folder; with none present it falls back to
alphabetical order, which scrambles the intended reading sequence.

Add a meta.json for each specification section that sets the section
title and lists its pages in the order given by mkdocs.yml, so the
rendered site matches the local mkdocs preview.

Signed-off-by: Matthias Lechner <[email protected]>
@github-actions

This comment was marked as outdated.

Comment thread system-design/specification/margo-management-interface/meta.json
@github-actions

This comment was marked as resolved.

@matlec
matlec force-pushed the feat/miaf-integration branch from 6ca5769 to 3415662 Compare July 24, 2026 12:03
matlec added 4 commits July 24, 2026 16:10
…rator

The Trust Domain can be derived from the
provisioned SVID alone by inspecting the
SPIFFE ID in the cert's URI SAN.

Signed-off-by: Matthias Lechner <[email protected]>

## X.509-SVID Profile

MIAF adopts the [SPIFFE X.509-SVID specification](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md) by reference. X.509-SVID is the SVID representation used throughout MIAF.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe would be good to mention if it pinned to a specific version. And how future versions are handled

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, this is already covered here.

| **RSA (≥3072 + SHA-256)** | Modulus MUST be at least 3072 bits; signatures MUST use SHA-256. RSASSA-PSS (`PS256`, [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017)) is RECOMMENDED. RSASSA-PKCS#1 v1.5 (`RS256`) MAY be used only for X.509 certificate and CSR signatures (today the only RSA signatures in MIAF), for compatibility with issuing CAs that cannot produce PSS signatures (its use there remains sanctioned by [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf)). Any other RSA signature MIAF defines later MUST use PSS. |

- **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components always share at least one algorithm.
- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Because a principal presents a single SVID bound to one signature algorithm, an SVID issued with a non-mandatory algorithm can be validated only by peers that implement that algorithm; a peer that does not will fail to validate it. Selecting an SVID signature algorithm that is supported across the Trust Domain is therefore the operator's responsibility at issuance (see [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 is always a safe choice.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Because a principal presents a single SVID bound to one signature algorithm" is technically imprecise. An SVID has:

  • a subject public-key algorithm
  • a CA certificate-signature algorithm

A MIAF profile could require the issuing CA to use the same algorithm family and parameters as the principal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the wording was imprecise and could mislead people reading the spec. I have reworded that paragraph so that it now covers the signature algorithm of each certificate in the chain and the public-key algorithm of the leaf key.

A MIAF profile could require the issuing CA to use the same algorithm family and parameters as the principal.

I'd rather not add this requirement. An enterprise root CA using RSA with leaf certificates using EC is a common enough pattern, so I'd be concerned to over-constrain here. We'll also need mixed chains for post quantum transition. The updated wording puts the responsibility on the operator. So if an operator wants to be on the safe side, they can always choose ECDSA P-256 with SHA-256 throughout the chain.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not the complete chain but at least the Issuing CA of the SVID?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh, I'm not sure what that would buy us. Suppose we have an RSA root with an EC intermediate issuing EC leaf certificates. The verifier still needs RSA because the intermediate's certificate is signed with the root's RSA key. The set of algorithms the verifier must implement doesn't shrink. Meanwhile the constraint would tie the issuing CA's key to the leaf's algorithm family. So I'd leave this as operator guidance.

matlec added 17 commits July 31, 2026 07:27
SPIFFE treats spiffe_sequence as ordering metadata, not an anti-rollback
control, and defaults to accepting the most recently retrieved bundle when
the field is absent. Rollback is already covered by authenticating every
retrieval and by the refresh interval overriding HTTP cache freshness.

Signed-off-by: Matthias Lechner <[email protected]>
Signed-off-by: Matthias Lechner <[email protected]>
...until we decide to register with IANA

Signed-off-by: Matthias Lechner <[email protected]>
Bootstrap retrieval of the discovery document and Trust Bundle cannot be
authenticated with an X.509-SVID at all, rather than merely happening before
a client holds one. The MIS presents no SVID, so the service impersonation
mitigation now points at initial-trust anchors and pins instead of at SVID
verification.

Signed-off-by: Matthias Lechner <[email protected]>
and ensure we consistently apply our own terminology

Signed-off-by: Matthias Lechner <[email protected]>
Adopt pre-draft's restored versioning policy and the roles-to-capabilities
change (#197). Conflict resolutions:

- device-requirements.md: took pre-draft's roles-to-capabilities rewrite and
  re-applied our removal of "onboarding" from the single-WFM sentence.
- api-requirements-and-security.md: kept our "API Definition" and "Transport"
  structure. Pre-draft's "Server-side TLS ... over HTTP1.1" line is superseded
  by our Transport section, which requires HTTP/1.1 and upgrades TLS to mTLS.
- workload-management-api: took pre-draft's 1.0.0-rc.2 name and version, kept
  our folded description block and our server URL without the trailing slash,
  and took pre-draft's "Workload Fleet Manager APIs" description.
- meta.json: the last pages entry named the OpenAPI file, which the rename
  made stale; it now names the management-interface-swagger page.

mkdocs build --strict introduces no new warnings against the pre-merge tip.
Signed-off-by: Matthias Lechner <[email protected]>

Terms introduced by MIAF:

- **Principal**: a non-human Margo component that holds, or is being provisioned with, a SPIFFE identity in a Trust Domain. Edge Compute Devices, WFMs, and WFM Clients are all principals.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment made within technical lexicon, is a Edge Compute Device a Principal? Or is simply the WFM Client?


#### Support for Extended Device Communications Downtime
Interface patterns MUST support extended device communication downtime.
## Extended Device Downtime

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this as a lingering requirement we placed in the specification a long time ago.

With the move to mTLS, does this section need removed or altered to fit the new pattern?

Our TWG feature assigned to supporting intermittently connected devices has not been placed as a GA1 feature that we enable.

The caller identity for every request is the authenticated WFM Client SPIFFE ID; the request itself does not carry it. A WFM derives the caller from the SPIFFE ID, not from any identifier in the request path or body.

#### Unique Identifiers
Every Management Interface endpoint is scoped to the authenticated caller. A WFM determines from the caller's identity which devices that client is responsible for and which deployments are assigned to them. Where a request path carries a resource identifier, for example `{deviceId}` or `{digest}`, the WFM looks that identifier up only among the resources in the caller's scope. A WFM MUST NOT expose or mutate a resource outside the caller's scope.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule assumes the caller's device scope already exists, but nothing defines how a deviceId becomes bound to a wfm-client-id. Today it appears to be trust-on-first-declaration: the client self-asserts deviceIds via POST /capabilities/{deviceId} and the WFM remembers them. Please specify: is the binding client-asserted or operator-registered? What prevents client X from POSTing capabilities under a deviceId owned by client Y?

I'm still struggling with the wfm-client-id and deviceId and how they are linked.


For **enrollment**, the operator:

1. accepts a CSR from the principal (the preferred path, since it keeps the private key on the principal and supports hardware-bound keys such as a TPM, secure element, or HSM). Where the principal cannot generate its own key pair, the operator generates one centrally and accepts the resulting concentration of key custody;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this leaving an open requirement for our devices that host the clients to support a hardware-bound key store?

I don't see a new requirement in the device requirements markdown, should there be? I think so.

| **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID whose Trust Domain is not the verifier's own. |
| **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](./tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. |
| **Forwarded-identity header injection** | In a TLS-offload topology, an attacker injects a forged `Client-Cert` header to impersonate an authenticated caller at the backend. | The proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header on incoming requests, and the backend MUST accept a forwarded identity only over the trusted proxy boundary, per [traffic-inspecting proxies](./tls-requirements.md#traffic-inspecting-proxies). |
| **Unreliable or manipulated verifier clock** | A principal mis-evaluates certificate validity periods because its clock is skewed or unset (a dead RTC battery, no time source at first boot) or because an attacker rewinds it, for example by spoofing an unauthenticated time source on the adversarial network. Rewinding the clock extends the validity window of an expired SVID, defeating the use of expiry as revocation. | SVID validation depends on a trustworthy local time source. A principal SHOULD synchronize time from a trustworthy source before validating certificates, and operators SHOULD account for devices with weak clocks when choosing SVID lifetimes (see [SVID lifetime guidance](./identity-lifecycle.md#svid-lifetime-guidance)). A deployment that cannot assure clock integrity SHOULD NOT rely on expiry alone for revocation. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this should result in a harder "MUST" requirement regarding time synchronization.

Would be added scope outside of this SUP, however, for critical functions like OTEL we are going to need accurate clocks in this architecture.

thoughts>?

Every Management Interface endpoint is scoped to the authenticated caller. A WFM determines from the caller's identity which devices that client is responsible for and which deployments are assigned to them. Where a request path carries a resource identifier, for example `{deviceId}` or `{digest}`, the WFM looks that identifier up only among the resources in the caller's scope. A WFM MUST NOT expose or mutate a resource outside the caller's scope.

The WFM MUST create a URL-safe client ID to uniquely identify each client within the architecture.
The WFM authorizes each request using local policy keyed on the authenticated WFM Client identity, and MAY deny a request from a still-valid credential, per [Authorization](../identity/wfm-identity-profile.md#authorization). When a WFM denies a request by local policy (for example, a retired client relationship), it SHOULD respond `403 Forbidden` with an [RFC 9457](https://datatracker.ietf.org/doc/html/rfc9457) Problem Details body (`Content-Type: application/problem+json`) using the `wfm-client-relationship-retired` type:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probabaly a task for after this PR gets merged, but Viresh is working on the PR to the spec to include these common Problems and is documenting them as "MUST".
See here

@vireshnavalli vireshnavalli Aug 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ajcraig @matlec @phil-abb
I will include @matlec open-api spec changes and problem types to my PR so that there is no need of new PR and it will eliminate rework later.


## Lifecycle Vocabulary

A MIAF identity moves through five lifecycle phases:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend numbering these for better claroty instead of bullet points.

nit picky I know sorry :)

For every change in deployment state - including installation, updates, removals, and failures - the client MUST report the corresponding status to the WFM using the [Deployment Status API](../margo-management-interface/deployment-status.md).

## Endpoints - State Manifest
## Endpoints: State Manifest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another comment along the lines of decoupling client ID<=>Device 1:1 relationship wording. Maybe the below comment isn't an issue.

The (device) client wording (and "the complete desired workload configuration assigned to the device" / "workloads assigned to the device" above) reads as a leftover from the 1:1 device==client model, but the inconsistency runs deeper than the phrasing:

GET /api/v1/deployments takes no deviceId, and the manifest has no per-device field — a single manifestVersion, one bundle, and a flat deployments[], all scoped to the authenticated client. Capabilities, by contrast, are reported per deviceId (POST /capabilities/{deviceId}, including gateway child devices). So the API is asymmetric: device-addressed inbound, client-addressed outbound.

For a client that fronts many devices (e.g., a gateway with deviceA/deviceB), please clarify how per-device desired state is delivered and versioned:

Is there intentionally one manifest per client covering all its devices? If so, how does a deployment get targeted to a specific deviceId — there's no device field in deployments[] — and is manifestVersion a single per-client sequence spanning all devices?
Or should desired state be per device (a deviceId selector plus per-device manifestVersion), matching the capabilities model?

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.

5 participants