-
Notifications
You must be signed in to change notification settings - Fork 0
Identity Adapter Contract
Normative contract for identity-projection adapters. The input/output schema, reference adapter specifications, SHACL projection shape, refresh-policy semantics, and new-provider extension guide live here. The
flexo-rtmDesign Spec §4.3 and §6.3 reference this page; tests undertests/conformance/test_identity_adapters.py,test_adapter_contract_schema.pyenforce it. See also ADR-025 Identity by Thin Projection of External Sources, Identity Boundaries and Policy Projections (rationale).
flexo-rtm does not authenticate users. It carries thin RDF projections of identities authored by external authoritative systems (institutional SSO via OIDC / SAML, LDAP / Active Directory, GitHub / GitLab, custom identity providers).
Adapters are thin — they translate provider-specific identity claims into the projection schema (§4 below). Adapters have no business logic; they do not arbitrate identity provider conflicts; they do not store credentials.
This contract specifies (a) the projection schema all adapters MUST produce, (b) the reference adapters shipped in v0.1, (c) the SHACL shape that validates conforming projections, (d) refresh-policy semantics, and (e) how to extend flexo-rtm to support a new provider.
- Source of truth lives outside. Employee records, role assignments, group memberships, attribute claims live in the institutional identity provider.
- Thin projections live inside. RDF projection captures just enough for SHACL/SPARQL policy evaluation.
-
One bottleneck. All identity references in
flexo-rtmgo through typed primitives (rtm:approvedBy,rtm:hasExternalIdentity). - Configurable per institution. Adopters wire their identity provider via an adapter conforming to this contract.
The output of any adapter is RDF using these vocabularies:
- W3C FOAF for persons (
foaf:Person,foaf:name,foaf:mbox) - W3C Org Ontology for organizations and memberships (
org:Organization,org:Membership,org:role,org:organization) - W3C PROV-O for projection provenance (
prov:wasGeneratedAtTime,prov:wasAttributedTo) -
rtm:namespace for the integration seams (external-identity strings, attributes, scope, policy)
:engineer-zargham a foaf:Person ;
rtm:hasExternalIdentity "github:zargham" , # required: at least one
"oidc:https://idp.example/sub-123" ; # multiple allowed
foaf:name "Michael Zargham" ; # optional
foaf:mbox <mailto:michael@example.org> ; # optional
org:hasMembership :membership-1 .
:membership-1 a org:Membership ;
org:role rtm:role/safety-engineer ; # required
org:organization :org-adcs ; # required
rtm:scopedTo rtm:scope/adcs-program ; # optional; if absent, scope-unbounded
rtm:hasAttribute :attr-clearance-secret . # optional; many allowed:attr-clearance-secret a rtm:Attribute ;
rtm:attributeKey "security-clearance" ;
rtm:attributeValue "SECRET" .Attribute values are typed by the adapter:
-
xsd:stringfor symbolic values (clearance levels, certification names) -
xsd:dateTimefor date-bounded attributes (training-completion-date) -
xsd:integer/xsd:decimalfor numerical attributes -
xsd:booleanfor flags
When attribute values are ordered (e.g., clearance levels: UNCLASSIFIED < CONFIDENTIAL < SECRET < TOP_SECRET), the adapter MUST provide a separate ordering declaration via rtm:attributeOrdering:
rtm:attributeKey/security-clearance rtm:attributeOrdering (
"UNCLASSIFIED" "CONFIDENTIAL" "SECRET" "TOP_SECRET"
) .Policies using rtm:attributeMinValue consult this ordering at SPARQL evaluation time.
:org-adcs a org:Organization ;
foaf:name "ADCS Engineering Team" ;
rtm:hasExternalIdentity "github-team:dynamicalsystemsgroup/adcs" ;
rtm:hasQualifiedRole rtm:role/qualified-auditor-aerospace , # optional; for §4.8 L3 audits
rtm:role/asot-holder . # optional; for ADR-030 ASOTOrganizations participate in scope ASOT designation (rtm:asotHeldBy) and qualified-role audits (§4.8 Level 3 of Design Spec).
Policies are RDF resources independent of any single identity. The projection contains policies the adapter has authority to attach to identities under its provider domain:
:policy-safety-attestation a rtm:Policy ;
rtm:appliesToRole rtm:role/safety-engineer ;
rtm:requiresAttribute [
rtm:attributeKey "security-clearance" ;
rtm:attributeMinValue "CONFIDENTIAL"
] ;
rtm:permitsAttestationType rtm:SufficiencyAttestation ;
rtm:permitsAspect rtm:safety ;
rtm:withinScope rtm:scope/adcs-program .A policy says: an identity with this role, satisfying these attribute predicates, may emit these attestation types for these aspects within these scopes.
Policy IRIs MUST be stable across refreshes. Policies SHOULD be versioned via owl:versionInfo when their content changes; new versions get new IRIs.
Every projection bundle SHOULD include provenance for the projection itself:
:projection-2026-05-18T14-30-00Z a prov:Entity ;
prov:wasGeneratedAtTime "2026-05-18T14:30:00Z"^^xsd:dateTime ;
prov:wasAttributedTo :github-adapter-v0-1 ;
prov:specializationOf rtm:identity-projection ;
rtm:refreshPolicy "every-cert-run" . # see §6The audit transcript references this entity to record the projection-as-of-cert-time.
flexo-rtm validates adapter output against ontology/shapes/identity-projection.shacl.ttl. The shape enforces:
rtm:PersonProjectionShape a sh:NodeShape ;
sh:targetClass foaf:Person ;
sh:property [
sh:path rtm:hasExternalIdentity ;
sh:minCount 1 ;
sh:datatype xsd:string ;
sh:message "Every projected Person requires at least one rtm:hasExternalIdentity string"
] .
rtm:MembershipProjectionShape a sh:NodeShape ;
sh:targetClass org:Membership ;
sh:property [
sh:path org:role ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:message "Every Membership requires an org:role IRI"
] ;
sh:property [
sh:path org:organization ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:message "Every Membership requires an org:organization IRI"
] .
rtm:AttributeProjectionShape a sh:NodeShape ;
sh:targetClass rtm:Attribute ;
sh:property [ sh:path rtm:attributeKey ; sh:minCount 1 ; sh:datatype xsd:string ] ;
sh:property [ sh:path rtm:attributeValue ; sh:minCount 1 ] .
rtm:PolicyProjectionShape a sh:NodeShape ;
sh:targetClass rtm:Policy ;
sh:property [ sh:path rtm:permitsAttestationType ; sh:minCount 1 ; sh:nodeKind sh:IRI ] ;
sh:property [ sh:path rtm:withinScope ; sh:minCount 1 ; sh:nodeKind sh:IRI ] .An adapter that produces RDF passing this shape is conforming.
When a new attestation is written, the following SHACL constraint evaluates applicable policies via SPARQL. This is the single point where authority is checked:
rtm:AttestationAuthorizationShape a sh:NodeShape ;
sh:targetClass rtm:Attestation ;
sh:sparql [
sh:message "Approver not authorized for this attestation under current policy" ;
sh:select """
PREFIX rtm: <https://example.org/rtm/>
PREFIX org: <http://www.w3.org/ns/org#>
SELECT $this WHERE {
$this rtm:approvedBy ?approver ;
a ?attestationType ;
rtm:appliesTo ?subject .
?subject rtm:hasAspect ?aspect ;
rtm:inScope ?scope .
FILTER NOT EXISTS {
?policy a rtm:Policy ;
rtm:permitsAttestationType ?attestationType ;
rtm:permitsAspect ?aspect ;
rtm:withinScope ?scopeMatch .
# Scope match: policy scope == subject scope OR subject scope is sub-scope
{ ?subject rtm:inScope ?scopeMatch }
UNION
{ ?subject rtm:inScope/rtm:extends* ?scopeMatch }
# Approver has the role
?approver org:hasMembership/org:role ?role .
?policy rtm:appliesToRole ?role .
# All attribute requirements satisfied
FILTER NOT EXISTS {
?policy rtm:requiresAttribute ?req .
?req rtm:attributeKey ?reqKey .
FILTER NOT EXISTS {
?approver org:hasMembership/rtm:hasAttribute ?attr .
?attr rtm:attributeKey ?reqKey .
# Value satisfies minValue ordering (if applicable)
# ... detailed value-check pattern; see implementation
}
}
}
}
"""
] .A failing constraint result indicates the approver is not authorized for the attestation under any policy. The attestation write is rejected at the SHACL gate.
Projections are point-in-time. Adopters configure refresh via rtm:refreshPolicy:
| Mode | Semantics | Cost | Use case |
|---|---|---|---|
every-cert-run |
Adapter is invoked each time the oracle runs certify; projection is current at cert time |
High (provider load + latency) | Always-current authorization |
on-commit |
Adapter invoked when the engineer commits an attestation; projection cached until next commit | Medium | Standard workflow |
scheduled |
Adapter runs on a cron (e.g., nightly); projection cached between runs | Low | Predictable cost, bounded staleness |
static |
Projection is loaded at startup and not refreshed | Zero | Test scenarios, audit replay |
The audit transcript records the active refresh policy and the projection-as-of-cert-time. Re-running the transcript months later evaluates against the recorded projection, not the live identity provider — per ADR-026 Reproducibility is Structural and Local, identity changes after cert do not invalidate past attestations.
Input: GitHub user data (REST API or GraphQL) for an authenticated query.
Mapping:
| GitHub field | Projection target |
|---|---|
login (e.g., "zargham") |
rtm:hasExternalIdentity "github:zargham" |
name |
foaf:name |
email (if public) |
foaf:mbox |
| GitHub Teams (per org) | one org:Membership per team; org:role = team name; org:organization = parent org |
Custom team labels (e.g., clearance:secret) |
rtm:hasAttribute with rtm:attributeKey/attributeValue derived from label |
Adapter location: oracle/src/oracle/identity/adapters/github.py
Conformance test: sample GitHub user payload + expected projection output; tests/conformance/test_identity_adapters.py::test_github_adapter.
Input: OIDC ID token + UserInfo endpoint payload.
Mapping:
| OIDC claim | Projection target |
|---|---|
sub |
rtm:hasExternalIdentity with oidc: prefix + issuer URI + sub |
name |
foaf:name |
email |
foaf:mbox |
groups (array of group names) |
one org:Membership per group; org:role derived from group name; org:organization derived from issuer or convention |
| Custom claims (configurable mapping) |
rtm:Attribute instances |
Configuration: YAML mapping file declaring which OIDC claims map to which rtm: predicates (so adopters can use their custom claims without code changes).
Adapter location: oracle/src/oracle/identity/adapters/oidc.py
Conformance test: sample OIDC ID token + UserInfo payload + custom-claim YAML + expected projection.
Input: Fulcio-issued OIDC token from GitHub Actions workflow context.
Mapping:
| Claim | Projection target |
|---|---|
sub (e.g., repo:dynamicalsystemsgroup/flexo-rtm:ref:refs/heads/main) |
ephemeral rtm:hasExternalIdentity with fulcio: prefix |
repository, workflow, ref
|
rtm:hasAttribute projecting CI context |
job_workflow_ref |
rtm:hasAttribute capturing the workflow file SHA |
The ephemeral identity is scoped to the workflow run — the projection has a short TTL and is intended for keyless-signed attestations only.
Adapter location: oracle/src/oracle/identity/adapters/gha_oidc.py
Writing a new adapter (e.g., SAML, LDAP, Okta, Auth0, Keycloak) requires:
- Input adapter — code that consumes the provider's native claim format (XML for SAML, LDIF for LDAP, JSON for Okta/Auth0/Keycloak)
- Field mapping — translate provider fields to projection schema (§3); document the mapping in the adapter's docstring
- Configuration surface — declarative YAML for any custom field mappings (so adopters can tune without code changes)
-
Conformance test — sample payload + expected projection RDF; place in
tests/conformance/test_identity_adapters.py - No core code changes — the oracle dispatches to adapters by configuration; adding an adapter is additive
The acceptance criterion for a new adapter: the projection it produces passes the SHACL shape in §4 AND a representative sample passes the policy SPARQL pattern in §5.
- No authentication. Adapters do not log in. They consume already-authenticated claims (the host application authenticates, then hands the adapter the claim payload).
- No credential storage. Adapters do not persist passwords, tokens, refresh tokens, certificates. Tokens are consumed and discarded.
- No business logic. Adapters do not decide who is authorized — they only project. Policy evaluation happens in SHACL via SPARQL.
- No provider-side writes. Adapters do not write back to the identity provider. The relationship is read-only.
-
No identity de-duplication. If the same human appears under multiple external identities (e.g., GitHub + OIDC), the projection includes both via multiple
rtm:hasExternalIdentityvalues on the samefoaf:Person; cross-provider de-duplication is the adopter's responsibility.
This contract pins to:
- W3C FOAF Vocabulary Specification 0.99 (Paddington Edition)
- W3C Org Ontology (October 2014)
- W3C PROV-O REC 2013
Future revisions of these vocabularies require a new contract version. v0.1 adapters MUST conform to the versions listed above.
- Flexo Git Coexistence
- ADCS Prototype Lessons
- MVC Pattern from RIME TRL ANT
- Human-AI Accountability
- Multi-Agent Discourse Graph Precedent
- OSLC RM and QM Review
- INCOSE V2 Review
- OMG SysMLv2
- PROV EARL GSN P-PLAN
- Dragon Architecture and Mission Enterprise
- Traditional Forward and Backward Analysis
- Attestation Infrastructure in v0.1
- Identity Boundaries and Policy Projections
- External URI References
- Signed Envelopes and Established Standards
- Aspect Coverage with Adequacy and Sufficiency
- Federated Audit and Composition
- Certification Predicate
- Gap Taxonomy
- Quantitative Outcomes
- Engineering Lifecycle Stages (v0.2)
- Topological Framework Future Work (research phase)
- Vertices Edges Faces (research phase)
- Three-Layer Architecture
- Operational Layer UX Discipline
- Storage Layer Flexo Conventions
- Analysis Layer Scope Algebra
- OSLC Roundtrip Acceptance
- Identity Adapter Contract
- Flexo REST Binding
- SysMLv2 Ingestion Contract
- External URI Rules
- Signed Envelope Shapes
- Parsimony Manifest
- Lossless Roundtrip Definition
- Vendor Extension Carry-Through
- OSLC RM Adapter Contract
- OSLC QM Adapter Contract
- ADR Template
- ADR-001 Foundations First Approach
- ADR-002 SysMLv2 Anchoring
- ADR-003 Topological Framework Documented as Future Work
- ADR-003a v0.1 Ships Traditional Analysis Only
- ADR-004 Quantitative Certification Outcome
- ADR-005 Adequacy and Sufficiency as Guidance Subtypes
- ADR-006 Three-Layer Architecture
- ADR-007 Scope as First-Class RDF Resource
- ADR-008 Repo Name and Org Transfer Plan
- ADR-009 Two-Repo Strategy
- ADR-010 OSLC-RM and OSLC-QM in v0.1
- ADR-011 Lossless Criterion A plus C
- ADR-012 Direct RDF Properties over Reified Edges
- ADR-013 Simplicial Complex as Derived View When Built
- ADR-014 Parsimony Layer Build-Time Extraction
- ADR-015 GSN Adoption for Adequacy and Sufficiency
- ADR-016 Composable SHACL Profiles
- ADR-017 knowledgecomplex as Optional Extras
- ADR-018 V minus F Invariant Deferred with Topological Framework
- ADR-019 Derived Binary View from Quantitative Metrics
- ADR-020 Vocabulary Alignment with Zargham 2026
- ADR-021 Three Attestation Subclasses Ship in v0.1
- ADR-022 External URI References as Open-Source Foundation
- ADR-023 Cryptography by Composition of Battle-Tested Standards
- ADR-024 Identity by Thin Projection of External Sources
- ADR-025 Reproducibility is Structural and Local
- ADR-026 Cryptographic Agility via Algorithm Profiles
- ADR-027 Bit-Exactness vs Numerical Tolerances Are Both First-Class
- ADR-028 Scope-Level Adequacy and Sufficiency for Federated Audit
- ADR-029 Engineering Lifecycle Stages as Scope Metadata
- ADR-030 Polycentric ASOT Authority Model
- ADR-031 Attestation Status Pass Fail Deferred Deprecated
- ADR-032 Methodology Agnosticism as Foundational Axiom
- ADR-033 Generalized ASOT Principle for All Identified Things