diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06b8885 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.venv/ +/.vscode/ +**/__pycache__/ +*.log +/generated/*/ +*.zip diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..431b02b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "ontology-management-base"] + path = ontology-management-base + url = git@github.com:GAIA-X4PLC-AAD/ontology-management-base.git +[submodule "service-characteristics"] + path = service-characteristics + url = https://gitlab.com/gaia-x/technical-committee/service-characteristics-working-group/service-characteristics.git diff --git a/README.md b/README.md index 3c40519..54fa25c 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,201 @@ -# DEMIM Credentials - -## Content -A public repository containing examples for (verifiable) credentials, associated json-ld context definitions and json manifests. The crendetials are used in the [Decentralized Digital Membership Management](https://identity.ascs.digital). -The DID of issuers and subjects and the UUIDs of the credentials have been aligned with the content of the following example [revocation registry](https://better-call.dev/ghostnet/KT1PZFXebyGvRFG8enbuVL9nrvTi4krYqeKt/storage.) - -## Examples -There are two types of json-ld examples for the credentials. The member credentials and the user credential. The member credential is used to e.g. register a company with an application like e.g. [Simpulse](https://simpulse.de) for creating the company profile with minimal validated information. The user credential is used in asc(s ecosystem applications to set initial rights and roles. -The examples are once given with an external context definition and also with the attributes defined inline in the credential context itself. This is necessary as third-party libraries like [didkit](https://github.com/spruceid/didkit) do not allow external context loading due to security implications. - -## Manifests -The manifest files are provided to render an identity card in a SSI wallet like e.g. [altme](https://altme.io) according to the identity foundation [wallet rendering specification](https://identity.foundation/wallet-rendering/). - -## Todos -The context json files need to be hosted at https://schema.ascs.digital/AscsUserCredential/v1. -All terms need to be hosted as .pdf files at https://media.ascs.digital/terms/. - -## Resources -* [Implementation Guide](https://www.w3.org/TR/vc-imp-guide/#creating-new-credential-types) -* [w3c credentials v1](https://www.w3.org/2018/credentials/v1) -* [w3c vc-json-schema](https://w3c.github.io/vc-json-schema/) -* [json schema specification](https://json-schema.org/specification) -* [public schemas](https://schema.org/) -* [transform tools](https://transform.tools/) -* [json-ld best practices](https://w3c.github.io/json-ld-bp/?specStatus=ED) -* [version 4 uuid](https://www.uuidgenerator.net/version4) -* [module: pkh-tezos](https://did.js.org/docs/api/modules/pkh_tezos/) -* [did-pkh-method-draft](https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md) -* [Multiassets](https://multiformats.io/) -* [Content Identifier (CID)](https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid) -* [POC Content Identifier](https://github.com/GAIA-X4PLC-AAD/poc-ipfs-content-identifier) +# SimpulseID Credentials for the ENVITED Ecosystem + +This repository contains the **Verifiable Credential (VC)** building blocks used by +[https://identity.ascs.digital/](https://identity.ascs.digital/) +to manage identities and memberships in the **ENVITED Ecosystem** of the +_Automotive Solution Center for Simulation e.V. (ASCS e.V.)_. + +The repository provides: + +- JSON-LD **contexts** for all SimpulseID credential types +- Example **Verifiable Credentials** (VC v2, OIDC4VP-ready) +- Example **did:web** DID documents for participants, programs, users, and admins +- **Wallet manifests** for card rendering in SSI wallets (e.g. Altme) +- RDF/OWL **ontologies** and SKOS vocabularies aligning with the Gaia-X Trust Framework and ENVITED Ecosystem Specifications (EVES) + +All of this is intended to be **publicly hostable** and consumable by wallets, verifiers and services in the ENVITED ecosystem. + +--- + +## Installation + +If you want to use the validation scripts from 📁 `ontology-management-base/src` then you need to isntall the following dependencies: + +```bash +# On Windows use python instead of python3 +sudo apt-get install python3-full +python3 -m venv .venv/ +source .venv/bin/activate # On Windows use: source .venv/Scripts/activate +python3 -m pip install -r ontology-management-base/requirements.txt +python3 -m pip install -r requirements.txt +# Example check +python3 ontology-management-base/src/check_jsonld_against_shacl_schema.py examples/simpulseid-administrator-credential.json +``` + +--- + +## Repository structure + +### `contexts/` + +JSON-LD context documents used by SimpulseID credentials, for example: + +- `SimpulseIdCredentials.json` – main context for: + - `simpulseid:Participant` + - `simpulseid:AscsBaseMembership` + - `simpulseid:AscsEnvitedMembership` + - `simpulseid:Administrator` + - `simpulseid:User` +- `HarbourCredentials.json` – additional context for status / revocation information +- SKOS / code list contexts (e.g. legal form vocabulary) + +These files are meant to be hosted under: + +- `https://schema.ascs.digital/...` + +and are referenced from the example credentials via their `@context` arrays. + +--- + +### `examples/` + +Example **Verifiable Credentials** that show how the contexts and ontologies are intended to be used. + +Typical credential subjects include: + +- **Participant** – organizational identity (e.g. BMW) +- **ASCS Base Membership** – base membership in ASCS e.V. +- **ASCS ENVITED Membership** – ENVITED program membership, linked to base membership +- **Administrator** – natural person with administrative rights in ENVITED / ASCS +- **User** – natural person with initial roles/rights in ENVITED ecosystem applications + +Each VC uses: + +- `https://www.w3.org/ns/credentials/v2` (VC Data Model v2) +- SimpulseID context from this repo +- Harbour context for `credentialStatus` +- `harbour:CRSetEntry` + `statusPurpose: "revocation"` for revocation status +- `gx:*` terms to stay compatible with the **Gaia-X Credential Format** and Trust Framework + +#### `examples/did-web/` + +Example **did:web DID documents** that correspond to identifiers used in the credentials, e.g.: + +- Participants (`did:web:did.ascs.digital:participants:...`) +- Programs (`did:web:did.ascs.digital:programs:...`) +- Users & administrators (`did:web:did.ascs.digital:users:...`) +- Services (`did:web:did.ascs.digital:services:...`) + +These demonstrate: + +- How organizational DIDs (ASCS, ENVITED programs, participants) are modelled +- How user/admin DIDs are defined _without leaking personal data_ +- How to support key rotation and multiple chains (e.g. Tezos + Etherlink/EVM) via `blockchainAccountId` + +In production, these DID documents are intended to be hosted under: + +- `https://did.ascs.digital/...` + +--- + +### `manifests/` + +Wallet **rendering manifests** for each credential type, following the +[Decentralized Identity Foundation Wallet Rendering specification](https://identity.foundation/wallet-rendering/). + +They are used by wallets like **Altme** to: + +- Render credential “cards” with titles, subtitles and key properties +- Show important fields such as: + - organization name, legal form, VAT ID + - membership program and hosting organization + - user/admin name, email, affiliation + - links to terms & conditions and privacy policies +- Map `credentialSubject` properties and dates (`issuanceDate`, `expirationDate`) to UI elements + +Each manifest references: + +- A SimpulseID schema / type (e.g. `simpulseid:Participant`) +- The issuer DID of the manifest (typically an ASCS did:web) + +--- + +### `ontologies/` + +RDF/OWL ontologies and vocabularies that define the **formal semantics** of SimpulseID types and properties, aligned with: + +- **Gaia-X Trust Framework 24.11** +- **ENVITED Ecosystem Specifications (EVES)** +- **schema.org** and **vCard** where appropriate + +Key elements include: + +- `SimpulseIdOntology.ttl` + + - Classes: + - `simpulseid:Participant` ⊑ `gx:LegalPerson`, `schema:Organization` + - `simpulseid:AscsBaseMembership`, `simpulseid:AscsEnvitedMembership` ⊑ `schema:ProgramMembership` + - `simpulseid:Administrator`, `simpulseid:User` ⊑ `gx:NaturalPerson`, `schema:Person` + - Program classes for base and ENVITED memberships + - Properties: + - `simpulseid:legalForm` → SKOS `simpulseid:LegalForm` concepts + - `simpulseid:termsAndConditions` → `gx:TermsAndConditions` resources + - `simpulseid:baseMembership` linking ENVITED membership to base membership + - Address modelling: + - `gx:Address` with **vCard** properties: + - `vcard:street-address` + - `vcard:postal-code` + - `vcard:locality` + - `vcard:region` + - `gx:countryCode` for ISO country codes + +- Legal form SKOS vocabulary (e.g. `legalForm-v1.jsonld`) + - Code list of legal forms (`AG`, `GmbH`, `LLC`, `BenCom`, etc.) + - Used via `simpulseid:LegalForm` and `simpulseid:legalForm` in credentials + +These ontologies are the **ground truth** for what the JSON-LD contexts and examples mean at RDF level. + +--- + +## Intended usage within `https://identity.ascs.digital/` + +The artifacts in this repository are used by the **ENVITED Ecosystem identity services** to: + +- Issue and verify **Gaia-X compatible** Verifiable Credentials +- Support **self-sovereign identity** login flows via the **SSI-to-OIDC bridge** +- Provide consistent semantics for: + - ENVITED participants (organizations) + - ASCS base memberships + - ENVITED program memberships + - Administrative and user roles +- Render credential cards in SSI wallets for a smooth UX + +Typical flow: + +1. A participant (organization) is onboarded and receives a **Participant VC**. +2. The organization receives **ASCS base membership** and optionally **ENVITED membership** credentials. +3. Individual administrators and users receive **Admin/User VCs**, bound to opaque did:web identifiers under `did.ascs.digital`. +4. Wallets like Altme use the **contexts** and **manifests** from this repo to display these credentials. +5. Services behind `identity.ascs.digital` use the **ontologies** and **Gaia-X compatible structures** to perform trust and membership checks. + +--- + +## References + +Some relevant specifications and resources: + +- W3C Verifiable Credentials Data Model v2 + +- W3C Verifiable Credential Vocabulary (VC v2) + +- Gaia-X Credential Format & Trust Framework (24.11) + +- DIF Wallet Rendering specification + +- JSON-LD 1.1 & best practices + + +- JSON Schema + +- schema.org + diff --git a/contexts/AscsMemberCredential.json b/contexts/AscsMemberCredential.json deleted file mode 100644 index 87f58f3..0000000 --- a/contexts/AscsMemberCredential.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "@version": 1.1, - "@protected": true, - "AscsMemberCredential": "https://schema.ascs.digital/AscsMemberCredential#", - "AscsIssuer": { - "@id": "https://schema.ascs.digital/AscsMemberCredential#AscsIssuer", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url" - } - }, - "AscsMember": { - "@id": "https://schema.ascs.digital/AscsMemberCredential#AscsMember", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url", - "address": "http://schema.org/address", - "vatID": "http://schema.org/vatID", - "isAscsMember": "http://schema.org/Boolean", - "isEnvitedMember": "http://schema.org/Boolean", - "privacyPolicy": "https://schema.org/termsOfService", - "articlesOfAssociation": "https://schema.org/termsOfService", - "contributionRules": "https://schema.org/termsOfService" - } - }, - "PostalAddress": { - "@id": "http://schema.org/PostalAddress", - "@context": { - "@version": 1.1, - "@protected": true, - "streetAddress": "http://schema.org/streetAddress", - "postalCode": "http://schema.org/postalCode", - "addressLocality": "http://schema.org/addressLocality", - "addressCountry": "https://schema.org/addressCountry" - } - } -} \ No newline at end of file diff --git a/contexts/AscsUserCredential.json b/contexts/AscsUserCredential.json deleted file mode 100644 index 2886fbc..0000000 --- a/contexts/AscsUserCredential.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "@version": 1.1, - "@protected": true, - "AscsUserCredential": "https://schema.ascs.digital/AscsUserCredential/v1#", - "AscsIssuer": { - "@id": "https://schema.ascs.digital/AscsUserCredential/v1#AscsIssuer", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url" - } - }, - "AscsUser": { - "@id": "https://schema.ascs.digital/AscsUserCredential/v1#AscsUser", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "email": "https://schema.org/email", - "address": "http://schema.org/address", - "isAscsMember": "http://schema.org/Boolean", - "isEnvitedMember": "http://schema.org/Boolean", - "privacyPolicy": "https://schema.org/termsOfService" - } - }, - "PostalAddress": { - "@id": "http://schema.org/PostalAddress", - "@context": { - "@version": 1.1, - "@protected": true, - "streetAddress": "http://schema.org/streetAddress", - "postalCode": "http://schema.org/postalCode", - "addressLocality": "http://schema.org/addressLocality", - "addressCountry": "https://schema.org/addressCountry" - } - } -} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..7e275bd --- /dev/null +++ b/examples/README.md @@ -0,0 +1,146 @@ +# SimpulseID Credential Examples + +This folder contains **reference examples** for all Verifiable Credentials used in the +ENVITED Ecosystem operated by ASCS e.V. + +These examples demonstrate: + +- the JSON-LD structure using the public contexts in `/contexts` +- the semantics defined in `/ontologies` +- correct use of did:web identifiers for: + - participants (organizations) + - ASCS programs (base membership, ENVITED membership) + - users and administrators (opaque non-PII identifiers) +- correct Gaia-X–compatible modelling of addresses, legal forms, and terms & conditions +- revocation status entries using the Harbour Credentials context + +The examples serve as a canonical blueprint for services integrating with +****. + +--- + +## Structure of the Examples + +### 1. Verifiable Credentials + +Each credential in this folder uses: + +- `https://www.w3.org/ns/credentials/v2` (VC Data Model v2) +- `simpulseid_context.jsonld` (main context) +- `harbour_context.jsonld` (credential status) +- `harbour:CRSetEntry` with `statusPurpose: "revocation"` + +Types included: + +- **Participant Credential** + Identity of an organization (e.g., BMW), aligned with Gaia-X `gx:LegalPerson`. + +- **ASCS Base Membership Credential** + Proof of base membership in ASCS e.V. + +- **ENVITED Membership Credential** + Extends the base membership and links to a program DID. + +- **Administrator Credential** + Natural person with elevated rights, issued and controlled by ASCS. + +- **User Credential** + Natural person affiliated with a participant, issued by the participant’s admin. + +All credentials use **did:web subject identifiers** for users and admins: + +- opaque +- non-PII +- key-rotation capable +- hosted under: + `https://did.ascs.digital/users/...` + +--- + +## 2. did:web Documents + +Examples under `examples/did-web/` illustrate: + +- Participant DIDs controlled by organizations + (`did:web:did.ascs.digital:participants:ascs`, `participants:bmw`, …) + +- Program DIDs controlled by ASCS + (`did:web:did.ascs.digital:programs:ascs-base-membership`, …) + +- User and Administrator DIDs: + Opaque, privacy-preserving identifiers that _only_ expose verification keys. + +Each DID document supports: + +- Tezos account (did:pkh) +- Etherlink/EVM account (`blockchainAccountId: eip155:42793:...`) +- Key rotation (through verificationMethod lists) + +No DID document contains personal data. + +--- + +## 3. Wallet Rendering Manifests + +The manifests in `/manifests` define how SSI wallets such as **Altme** render cards using the +Decentralized Identity Foundation **Wallet Rendering Specification**. + +Each manifest: + +- references the correct SimpulseID credential type +- defines which properties appear on the card +- includes human-readable fallback titles +- is issued by the ASCS organizational DID (`did:web:did.ascs.digital:participants:ascs`) + +--- + +## 4. Notes on Issuance Model + +### Participant Credentials + +Issued by ASCS upon onboarding of an organization into the ENVITED ecosystem. + +### Program Membership Credentials + +Base membership and ENVITED membership are issued by ASCS. + +### Administrator Credentials + +Issued by ASCS to individuals acting on behalf of ASCS or participants. + +### User Credentials + +Issued by participant administrators to individuals. +These credentials use an opaque user DID under `did.ascs.digital` to support: + +- privacy (no PII in DID) +- key rotation +- multi-chain keys (Tezos + Etherlink) + +--- + +## 5. Revocation + +Example credentials reference a `credentialStatus` entry: + +- `harbour:CRSetEntry` +- `statusPurpose: "revocation"` +- `id` pointing to a `did:web` revocation registry fragment + +The DID document for the registry includes a **service endpoint** pointing to the actual registry. + +--- + +## 6. Tooling + +You may use: + +- **jsonld-cli** for JSON-LD normalization and checking +- **didkit** for signing / verifying VC Data Model v2 credentials +- **jq** for inspection and debugging +- **JSON Schema** for linting examples where applicable + +--- + +This folder is meant as a **reference implementation** for developers integrating +SimpulseID credentials into ENVITED applications and services. diff --git a/examples/did-web/README.md b/examples/did-web/README.md new file mode 100644 index 0000000..975b0c5 --- /dev/null +++ b/examples/did-web/README.md @@ -0,0 +1,245 @@ +# Example did:web Documents for SimpulseID + +This folder contains example **did:web DID documents** for entities used in the +SimpulseID / ENVITED identity ecosystem. These examples demonstrate how program, +participant, administrator, and user identifiers are published under the +`did.ascs.digital` domain. + +All documents in this folder are **examples only** and contain placeholder JWS +signatures (`EXAMPLE_SIGNATURE_*`) and placeholder JWK values. When deploying +these DIDs, replace the placeholder signatures and keys with real material +generated by the appropriate entity (ASCS or delegated admin user). + +--- + +## Core Trust Model (SimpulseID Best Practice) + +### did:web control + +SimpulseID uses **did:web**, which means: + +- The **controller in practice is always ASCS**, because ASCS operates the + `did.ascs.digital` web server. +- All DID Documents declare: + + ```json + "controller": "did:web:did.ascs.digital:services:trust-anchor" + ``` + + This expresses that all published DIDs are governed by the **ASCS trust + anchor service**. + +### Signatures = Attestations + +The `proof` section in each DID Document: + +- **does not grant control**, +- it **attests** that ASCS (or BMW, where relevant) approves the content. + +The true controlling entity for all did:web documents is always the +`services:trust-anchor` DID, because it owns the hosting infrastructure. + +--- + +## Program DID Documents + +Programs represent SimpulseID system-level definitions. They have stable +identifiers: + +- `did:web:did.ascs.digital:programs:ascs-base-membership` +- `did:web:did.ascs.digital:programs:ascs-envited-membership` +- `did:web:did.ascs.digital:programs:simpulseid-user` +- `did:web:did.ascs.digital:programs:simpulseid-administrator` + +They must be hosted at: + +- `https://did.ascs.digital/programs//did.json` + +Programs are always: + +- **controlled by**: `services:trust-anchor` +- **signed by**: `participants:ascs` +- **never storing personal keys** +- **never delegating signing authority** + +--- + +## Participant DIDs + +Participant DIDs represent **Gaia-X Legal Persons**, such as: + +- `did:web:did.ascs.digital:participants:ascs` +- `did:web:did.ascs.digital:participants:bmw` + +Participant DIDs contain: + +- organizational metadata (`schema:Organization`, `gx:LegalPerson`) +- a list of **delegated admin user keys** in `verificationMethod` +- **active delegated admin keys** in `assertionMethod` +- **revoked delegated admin keys** in `verificationMethod` + with a `"revoked"` timestamp + +### Important: BMW has no own cryptographic keys + +BMW intentionally **does not hold a participant-level key**. + +Instead: + +- **admin users sign “on behalf of BMW”** +- BMW’s DID lists **active admin keys** under `assertionMethod` +- Old admin keys remain listed with: + + ```json + "revoked": "" + ``` + + but their `id` (the hash fragment) stays **unchanged** so that + any existing references from credentials remain valid. + +### Hash fragment naming convention + +Because DID URLs with hash fragments are referenced from credentials and other +documents, **fragments MUST be stable over the lifetime of the key**. They +MUST NOT be renamed when the key is revoked. + +SimpulseID uses a simple, incremental naming scheme per network: + +```uri +#-key- +``` + +Examples: + +- `#tezos-key-1` +- `#etherlink-key-1` +- `#etherlink-key-2` + +When a key is revoked: + +- keep the `id` (e.g. `#etherlink-key-2`) unchanged, +- add a `"revoked": ""` field to the corresponding + `verificationMethod` entry, +- remove that key from `assertionMethod`. + +--- + +## User DIDs + +User DIDs live at: + +- `did:web:did.ascs.digital:users/` + +Properties: + +- **DID controlled by**: `services:trust-anchor` +- **keys controlled by the user**: + + ```json + "controller": "did:web:did.ascs.digital:users/" + ``` + +- used for personal authentication and signatures +- contain no organization metadata + +User DID Documents are signed by ASCS as attestation of correct structure. + +--- + +## Administrator DIDs + +An administrator is **not** a different DID type. + +Admin status is expressed through: + +- an **AdministratorCredential** issued by ASCS +- Harbour CRSet revocation for demotion +- BMW (or other participant) updating its participant DID to include the + admin user's keys in `assertionMethod` + +Admin promotion/demotion is reflected only in: + +- **credentials** +- **participant DID** (delegated key listing) + +The user DID does **not** structurally change when becoming an admin. + +--- + +## Trust Anchor DID + +`did:web:did.ascs.digital:services:trust-anchor` is the authoritative DID that: + +- asserts ownership of the SimpulseID infrastructure +- governs all did:web documents hosted under `did.ascs.digital` +- signs all program, participant, user, admin, and service DIDs + +All other DIDs reference: + +```json +"controller": "did:web:did.ascs.digital:services:trust-anchor" +``` + +--- + +## Revocation Registry DID + +The revocation registry lives at: + +- `did:web:did.ascs.digital:services:revocation-registry` + +It contains a `CRSetRevocationRegistryService` with: + +- CRSet registry API URL +- blockchain contract URN +- GitHub repository link +- scientific publication reference + +Credentials refer to status entries using: + +```json +"credentialStatus": { + "type": "harbour:CRSetEntry", + "id": "did:web:did.ascs.digital:services:revocation-registry#" +} +``` + +--- + +## Deployment Notes + +To deploy these DIDs in production: + +1. Store each DID at: + + ```url + https://did.ascs.digital/.../did.json + ``` + +2. Replace placeholder values: + + - `EXAMPLE_SIGNATURE_*` + - JWKs + - blockchain addresses + +3. Ensure: + + - MIME type `application/did+json` + - correct hosting paths + - TLS integrity + +4. For key rotation: + + - add new admin user keys + - update `assertionMethod` + - annotate old keys with `"revoked"` (without renaming their `id`) + +5. For admin lifecycle: + + - issue or revoke `AdministratorCredential` + - update participant DID key status accordingly + +--- + +These did:web examples form the public reference implementation for the +SimpulseID trust anchor, identity lifecycle, and delegation model used in the +ENVITED Ecosystem. diff --git a/examples/did-web/simpulseid-participant-bmw-did.json b/examples/did-web/simpulseid-participant-bmw-did.json new file mode 100644 index 0000000..5acf70e --- /dev/null +++ b/examples/did-web/simpulseid-participant-bmw-did.json @@ -0,0 +1,88 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org" + ], + "id": "did:web:did.ascs.digital:participants:bmw", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "alsoKnownAs": [ + "https://did.ascs.digital/participants/bmw" + ], + "verificationMethod": [ + { + "id": "did:web:did.ascs.digital:participants:bmw#tezos-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e", + "blockchainAccountId": "tezos:NetXnHfVqm9iesp:tz1SfdVU1mor3Sgej3FmmwMH4HM1EjTzqqeE", + "revoked": "2025-08-06T10:15:00Z" + }, + { + "id": "did:web:did.ascs.digital:participants:bmw#tezos-key-2", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "blockchainAccountId": "tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8" + }, + { + "id": "did:web:did.ascs.digital:participants:bmw#etherlink-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "blockchainAccountId": "eip155:42793:0x71C7656EC7ab88b098defB751B7401B5f6d8976F" + } + ], + "assertionMethod": [ + "did:web:did.ascs.digital:participants:bmw#tezos-key-2", + "did:web:did.ascs.digital:participants:bmw#etherlink-key-1" + ], + "service": [ + { + "id": "did:web:did.ascs.digital:participants:bmw#org-metadata", + "type": "OrganizationMetadataService", + "serviceEndpoint": "https://www.bmwgroup.com/" + } + ], + "organization": { + "@context": [ + "https://schema.ascs.digital/SimpulseId/v1/credentials", + "https://schema.org" + ], + "@id": "did:web:did.ascs.digital:participants:bmw", + "type": [ + "simpulseid:Participant", + "gx:LegalPerson", + "schema:Organization" + ], + "legalName": "Bayerische Motoren Werke Aktiengesellschaft", + "legalForm": "AG", + "registrationNumber": { + "@type": "gx:VatID", + "countryCode": "DE", + "vatID": "DE129273398" + }, + "duns": "313995269", + "email": "imprint@bmw.com", + "website": "https://www.bmwgroup.com/", + "legalAddress": { + "@type": "gx:Address", + "streetAddress": "Petuelring 130", + "postalCode": "80809", + "addressLocality": "München", + "countrySubdivisionCode": "DE-BY", + "country": "DE" + }, + "headquartersAddress": { + "@type": "gx:Address", + "streetAddress": "Petuelring 130", + "postalCode": "80809", + "addressLocality": "München", + "countrySubdivisionCode": "DE-BY", + "country": "DE" + } + }, + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:20:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_PAYLOAD_BMW" + } +} diff --git a/examples/did-web/simpulseid-program-administrator-did.json b/examples/did-web/simpulseid-program-administrator-did.json new file mode 100644 index 0000000..aae9b00 --- /dev/null +++ b/examples/did-web/simpulseid-program-administrator-did.json @@ -0,0 +1,41 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org", + "https://schema.ascs.digital/SimpulseId/v1/credentials" + ], + "id": "did:web:did.ascs.digital:programs:simpulseid-administrator", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "service": [ + { + "id": "did:web:did.ascs.digital:programs:simpulseid-administrator#program-metadata", + "type": "ProgramMetadataService", + "serviceEndpoint": { + "@type": [ + "schema:CreativeWork", + "simpulseid:AdministratorProgram" + ], + "@id": "https://did.ascs.digital/programs/simpulseid-administrator", + "name": "SimpulseID Administrator Program", + "description": "Program definition for SimpulseID administrators who manage participant onboarding, user roles, and membership credentials within the ENVITED Ecosystem.", + "about": "Administrator-level SimpulseID credentials with elevated permissions for managing organizations and memberships.", + "publisher": { + "@type": "schema:Organization", + "name": "Automotive Solution Center for Simulation e.V. (ASCS)", + "url": "https://ascs.digital/" + }, + "inLanguage": "en", + "datePublished": "2025-08-05", + "termsOfService": "https://media.ascs.digital/terms/simpulse_id_terms_2025-08-05.pdf#cidv1", + "privacyPolicy": "https://media.ascs.digital/terms/administrator_privacy_policy_2025-08-05.pdf#cidv1" + } + } + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:02:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFzY3MtZGlkLXdlYi1rZXktMSJ9.EXAMPLE_SIGNATURE_SIMPULSEID_ADMIN_PROGRAM" + } +} diff --git a/examples/did-web/simpulseid-program-ascs-base-membership-did.json b/examples/did-web/simpulseid-program-ascs-base-membership-did.json new file mode 100644 index 0000000..09e7d8c --- /dev/null +++ b/examples/did-web/simpulseid-program-ascs-base-membership-did.json @@ -0,0 +1,42 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org" + ], + "id": "did:web:did.ascs.digital:programs:ascs-base-membership", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "alsoKnownAs": [ + "https://did.ascs.digital/programs/ascs-base-membership" + ], + "service": [ + { + "id": "did:web:did.ascs.digital:programs:ascs-base-membership#program-metadata", + "type": "ProgramMetadataService", + "serviceEndpoint": "https://did.ascs.digital/programs/ascs-base-membership" + } + ], + "program": { + "@context": "https://schema.org", + "@type": [ + "schema:Program", + "simpulseid:BaseMembershipProgram" + ], + "@id": "https://did.ascs.digital/programs/ascs-base-membership", + "name": "ASCS e.V. Base Membership", + "description": "Base membership program of the Automotive Solution Center for Simulation e.V. (ASCS e.V.).", + "hostingOrganization": { + "@id": "did:web:did.ascs.digital:participants:ascs", + "@type": "Organization", + "name": "Automotive Solution Center for Simulation e.V." + }, + "articlesOfAssociation": "https://media.ascs.digital/terms/ascs_articles_of_association_2021-09-17.pdf#cidv1", + "contributionRules": "https://media.ascs.digital/terms/ascs_contribution_rules_2020-07-08.pdf#cidv1" + }, + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:00:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_PAYLOAD" + } +} diff --git a/examples/did-web/simpulseid-program-ascs-envited-membership-did.json b/examples/did-web/simpulseid-program-ascs-envited-membership-did.json new file mode 100644 index 0000000..8d8391a --- /dev/null +++ b/examples/did-web/simpulseid-program-ascs-envited-membership-did.json @@ -0,0 +1,48 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org" + ], + "id": "did:web:did.ascs.digital:programs:ascs-envited-membership", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "alsoKnownAs": [ + "https://did.ascs.digital/programs/ascs-envited-membership" + ], + "service": [ + { + "id": "did:web:did.ascs.digital:programs:ascs-envited-membership#program-metadata", + "type": "ProgramMetadataService", + "serviceEndpoint": "https://did.ascs.digital/programs/ascs-envited-membership" + } + ], + "program": { + "@context": "https://schema.org", + "@type": [ + "schema:Program", + "simpulseid:EnvitedMembershipProgram" + ], + "@id": "https://did.ascs.digital/programs/ascs-envited-membership", + "name": "ASCS e.V. ENVITED Membership", + "description": "ENVITED membership program of the Automotive Solution Center for Simulation e.V. (ASCS e.V.), providing access to the ENVITED ecosystem and related services.", + "hostingOrganization": { + "@id": "did:web:did.ascs.digital:participants:ascs", + "@type": "Organization", + "name": "Automotive Solution Center for Simulation e.V." + }, + "articlesOfAssociation": "https://media.ascs.digital/terms/ascs_articles_of_association_2021-09-17.pdf#cidv1", + "contributionRules": "https://media.ascs.digital/terms/ascs_contribution_rules_2020-07-08.pdf#cidv1", + "ecosystem": { + "@type": "schema:DigitalEcosystem", + "name": "ENVITED-X Data Space", + "url": "https://envited-x.net/", + "description": "The ENVITED-X Data Space is a collaborative platform for secure data sharing and digital asset management in the ENVITED ecosystem." + } + }, + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:05:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_PAYLOAD_ENVITED" + } +} diff --git a/examples/did-web/simpulseid-program-user-did.json b/examples/did-web/simpulseid-program-user-did.json new file mode 100644 index 0000000..f27f701 --- /dev/null +++ b/examples/did-web/simpulseid-program-user-did.json @@ -0,0 +1,41 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org", + "https://schema.ascs.digital/SimpulseId/v1/credentials" + ], + "id": "did:web:did.ascs.digital:programs:simpulseid-user", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "service": [ + { + "id": "did:web:did.ascs.digital:programs:simpulseid-user#program-metadata", + "type": "ProgramMetadataService", + "serviceEndpoint": { + "@type": [ + "schema:CreativeWork", + "simpulseid:UserProgram" + ], + "@id": "https://did.ascs.digital/programs/simpulseid-user", + "name": "SimpulseID User Program", + "description": "Program definition for standard SimpulseID users in the ENVITED Ecosystem. Covers basic identity, login, and membership-related functionality for natural persons.", + "about": "User-level SimpulseID credentials and their usage within the ENVITED data space.", + "publisher": { + "@type": "schema:Organization", + "name": "Automotive Solution Center for Simulation e.V. (ASCS)", + "url": "https://ascs.digital/" + }, + "inLanguage": "en", + "datePublished": "2025-08-05", + "termsOfService": "https://media.ascs.digital/terms/simpulse_id_terms_2025-08-05.pdf#cidv1", + "privacyPolicy": "https://media.ascs.digital/terms/privacy_policy_2025-08-05.pdf#cidv1" + } + } + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:00:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFzY3MtZGlkLXdlYi1rZXktMSJ9.EXAMPLE_SIGNATURE_SIMPULSEID_USER_PROGRAM" + } +} diff --git a/examples/did-web/simpulseid-service-revocation-registry-did.json b/examples/did-web/simpulseid-service-revocation-registry-did.json new file mode 100644 index 0000000..169231d --- /dev/null +++ b/examples/did-web/simpulseid-service-revocation-registry-did.json @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.reachhaven.com/Harbour/v1/credentials" + ], + "id": "did:web:did.ascs.digital:services:revocation-registry", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "service": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#crset", + "type": "CRSetRevocationRegistryService", + "serviceEndpoint": { + "@type": "harbour:CRSetServiceEndpoint", + "endpoint": "https://gatehouse.reachhaven.com/services/harbour/crset", + "statusPurpose": "revocation", + "contractURN": "urn:blockchain:eip155:42793:contract:0x646B92C8f21e55DF67E766047E4bD7bEdF8DfA14", + "sourceRepository": "https://github.com/ASCS-eV/smart-contracts", + "implementation": "https://arxiv.org/abs/2501.17089" + } + } + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:30:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFzY3MtZXRoZXJsaW5rLWtleS0xIn0.EXAMPLE_SIGNATURE_REVOCATION_REGISTRY" + } +} diff --git a/examples/did-web/simpulseid-service-trust-anchor-did.json b/examples/did-web/simpulseid-service-trust-anchor-did.json new file mode 100644 index 0000000..c89428f --- /dev/null +++ b/examples/did-web/simpulseid-service-trust-anchor-did.json @@ -0,0 +1,54 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://schema.org" + ], + "id": "did:web:did.ascs.digital:services:trust-anchor", + "controller": "did:web:did.ascs.digital:participants:ascs", + "verificationMethod": [ + { + "id": "did:web:did.ascs.digital:participants:ascs#tezos-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:participants:ascs", + "blockchainAccountId": "tezos:NetXnHfVqm9iesp:tz1ZBYB7Lwmoc7xbwq59mHK4GbiPhfPaEo2g" + }, + { + "id": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:participants:ascs", + "blockchainAccountId": "eip155:42793:0x71C7656EC7ab88b098defB751B7401B5f6d8976F" + } + ], + "assertionMethod": [ + "did:web:did.ascs.digital:participants:ascs#tezos-key-1", + "did:web:did.ascs.digital:participants:ascs#etherlink-key-1" + ], + "service": [ + { + "id": "did:web:did.ascs.digital:services:trust-anchor#owner", + "type": [ + "TrustAnchorService", + "OrganizationMetadataService" + ], + "serviceEndpoint": { + "@type": "schema:Organization", + "@id": "did:web:did.ascs.digital:participants:ascs", + "name": "Automotive Solution Center for Simulation e.V. (ASCS)", + "url": "https://ascs.digital/", + "description": "Trust anchor and operator of the SimpulseID / ENVITED identity infrastructure at did.ascs.digital.", + "contactPoint": { + "@type": "schema:ContactPoint", + "contactType": "support", + "email": "info@ascs.digital" + } + } + } + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:20:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:ascs#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_PAYLOAD_TRUST_ANCHOR" + } +} diff --git a/examples/did-web/simpulseid-user-21c7c8bc-6860-490b-8ec7-219c89d93e2c-did.json b/examples/did-web/simpulseid-user-21c7c8bc-6860-490b-8ec7-219c89d93e2c-did.json new file mode 100644 index 0000000..e50013b --- /dev/null +++ b/examples/did-web/simpulseid-user-21c7c8bc-6860-490b-8ec7-219c89d93e2c-did.json @@ -0,0 +1,36 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1" + ], + "id": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "verificationMethod": [ + { + "id": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#tezos-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "blockchainAccountId": "tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8" + }, + { + "id": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#etherlink-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "blockchainAccountId": "eip155:42793:0x71C7656EC7ab88b098defB751B7401B5f6d8976F" + } + ], + "authentication": [ + "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#tezos-key-1", + "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#etherlink-key-1" + ], + "assertionMethod": [ + "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#tezos-key-1", + "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c#etherlink-key-1" + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:05:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:bmw#etherlink-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_BMW_ADMIN" + } +} diff --git a/examples/did-web/simpulseid-user-44b982bb-ae61-4f6f-899f-a0982aaf367e-did.json b/examples/did-web/simpulseid-user-44b982bb-ae61-4f6f-899f-a0982aaf367e-did.json new file mode 100644 index 0000000..f632930 --- /dev/null +++ b/examples/did-web/simpulseid-user-44b982bb-ae61-4f6f-899f-a0982aaf367e-did.json @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1" + ], + "id": "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e", + "controller": "did:web:did.ascs.digital:services:trust-anchor", + "verificationMethod": [ + { + "id": "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e#tezos-key-1", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e", + "blockchainAccountId": "tezos:NetXnHfVqm9iesp:tz1SfdVU1mor3Sgej3FmmwMH4HM1EjTzqqeE" + } + ], + "authentication": [ + "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e#tezos-key-1" + ], + "assertionMethod": [ + "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e#tezos-key-1" + ], + "proof": { + "type": "EcdsaSecp256k1Signature2019", + "created": "2025-08-06T10:10:00Z", + "verificationMethod": "did:web:did.ascs.digital:participants:bmw#tezos-key-1", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE_SIGNATURE_BMW_ADMIN" + } +} diff --git a/examples/member-credential-full.json b/examples/member-credential-full.json deleted file mode 100644 index a763fc2..0000000 --- a/examples/member-credential-full.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - { - "@version": 1.1, - "@protected": true, - "AscsMemberCredential": "https://schema.ascs.digital/AscsMemberCredential/v1#", - "AscsIssuer": { - "@id": "https://schema.ascs.digital/AscsMemberCredential/v1#AscsIssuer", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url" - } - }, - "AscsMember": { - "@id": "https://schema.ascs.digital/AscsMemberCredential/v1#AscsMember", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url", - "address": "http://schema.org/address", - "vatID": "http://schema.org/vatID", - "isAscsMember": "http://schema.org/Boolean", - "isEnvitedMember": "http://schema.org/Boolean", - "privacyPolicy": "https://schema.org/termsOfService", - "articlesOfAssociation": "https://schema.org/termsOfService", - "contributionRules": "https://schema.org/termsOfService" - } - }, - "PostalAddress": { - "@id": "http://schema.org/PostalAddress", - "@context": { - "@version": 1.1, - "@protected": true, - "streetAddress": "http://schema.org/streetAddress", - "postalCode": "http://schema.org/postalCode", - "addressLocality": "http://schema.org/addressLocality", - "addressCountry": "https://schema.org/addressCountry" - } - } - } - ], - "type": [ - "VerifiableCredential", - "AscsMemberCredential" - ], - "issuanceDate": "2023-11-22T17:14:33Z", - "expirationDate": "2102-09-15T17:14:33Z", - "id": "urn:uuid:576fbefb-35e8-4b71-bb1a-53d1803c86de", - "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1ggujjYjA7oYoaZBzTg1tYSXn3VMjcgDuv", - "type": "AscsIssuer", - "name": "Automotive Solution Center for Simulation e.V.", - "url": "https://identity.ascs.digital/" - }, - "credentialSubject": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8", - "type": "AscsMember", - "name": "Testcompany GmbH", - "url": "https://test.de/", - "address": { - "type": "PostalAddress", - "streetAddress": "Teststraße 1", - "postalCode": "12345", - "addressLocality": "Munich", - "addressCountry": "DE" - }, - "vatID": "DE123456789", - "isAscsMember": true, - "isEnvitedMember": true, - "privacyPolicy": "https://media.ascs.digital/terms/ascs_privacy_policy_2020-07-08.pdf#cidv1", - "articlesOfAssociation": "https://media.ascs.digital/terms/ascs_articles_of_association_2021-09-17.pdf#cidv1", - "contributionRules": "https://media.ascs.digital/terms/ascs_contribution_rules_2020-07-08.pdf#cidv1" - } -} \ No newline at end of file diff --git a/examples/member-credential.json b/examples/member-credential.json deleted file mode 100644 index 3e7c2a7..0000000 --- a/examples/member-credential.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://schema.ascs.digital/AscsMemberCredential/v1" - ], - "type": [ - "VerifiableCredential", - "AscsMemberCredential" - ], - "issuanceDate": "2023-11-22T17:14:33Z", - "expirationDate": "2102-09-15T17:14:33Z", - "id": "urn:uuid:576fbefb-35e8-4b71-bb1a-53d1803c86de", - "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1ggujjYjA7oYoaZBzTg1tYSXn3VMjcgDuv", - "type": "AscsIssuer", - "name": "Automotive Solution Center for Simulation e.V.", - "url": "https://identity.ascs.digital/" - }, - "credentialSubject": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8", - "type": "AscsMember", - "name": "Testcompany GmbH", - "url": "https://test.de/", - "address": { - "type": "PostalAddress", - "streetAddress": "Teststraße 1", - "postalCode": "12345", - "addressLocality": "Munich", - "addressCountry": "DE" - }, - "vatID": "DE123456789", - "isAscsMember": true, - "isEnvitedMember": true, - "privacyPolicy": "https://media.ascs.digital/terms/ascs_privacy_policy_2020-07-08.pdf#cidv1", - "articlesOfAssociation": "https://media.ascs.digital/terms/ascs_articles_of_association_2021-09-17.pdf#cidv1", - "contributionRules": "https://media.ascs.digital/terms/ascs_contribution_rules_2020-07-08.pdf#cidv1" - } -} \ No newline at end of file diff --git a/examples/simpulseid-administrator-credential.json b/examples/simpulseid-administrator-credential.json new file mode 100644 index 0000000..b9ec90e --- /dev/null +++ b/examples/simpulseid-administrator-credential.json @@ -0,0 +1,61 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schema.ascs.digital/SimpulseId/v1/credentials#", + "https://schema.reachhaven.com/Harbour/v1/credentials#" + ], + "type": [ + "VerifiableCredential", + "harbour:VerifiableCredential", + "simpulseid:AdministratorCredential" + ], + "id": "urn:uuid:9d3a0c1b-4d4e-4f9a-9b0c-1d2e3f4a5b6d", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "type": "simpulseid:Issuer", + "member": "did:web:did.ascs.digital:participants:ascs" + }, + "issuanceDate": "2025-08-06T10:15:22Z", + "expirationDate": "2030-08-05T00:00:00Z", + "credentialSubject": { + "id": "did:web:did.ascs.digital:users:21c7c8bc-6860-490b-8ec7-219c89d93e2c", + "type": [ + "simpulseid:Administrator", + "gx:NaturalPerson", + "schema:Person" + ], + "memberOf": [ + "did:web:did.ascs.digital:participants:bmw", + "did:web:did.ascs.digital:programs:simpulseid-administrator" + ], + "givenName": "Andreas", + "familyName": "Admin", + "email": "andreas.admin@bmw.com", + "address": { + "@type": "gx:Address", + "streetAddress": "Petuelring 130", + "postalCode": "80809", + "locality": "München", + "countryCode": "DE", + "region": "DE21" + }, + "termsAndConditions": [ + { + "@id": "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq", + "@type": "gx:TermsAndConditions", + "gx:url": { + "@value": "https://media.ascs.digital/terms/administrator_privacy_policy_2025-08-05.pdf", + "@type": "xsd:anyURI" + }, + "gx:hash": "bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq" + } + ] + }, + "credentialStatus": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#f8b8a8150acbbbf936df9692ed7ca809c9a6a66b190149ce9d4e9557587829ec", + "type": "harbour:CRSetEntry", + "statusPurpose": "revocation" + } + ] +} diff --git a/examples/simpulseid-ascs-base-membership-credential.json b/examples/simpulseid-ascs-base-membership-credential.json new file mode 100644 index 0000000..99918b5 --- /dev/null +++ b/examples/simpulseid-ascs-base-membership-credential.json @@ -0,0 +1,59 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schema.ascs.digital/SimpulseId/v1/credentials#", + "https://schema.reachhaven.com/Harbour/v1/credentials#" + ], + "type": [ + "VerifiableCredential", + "harbour:VerifiableCredential", + "simpulseid:AscsBaseMembershipCredential" + ], + "id": "urn:uuid:7f3f7c6a-4b4d-4e9e-8f0a-9b1b2c3d4e5f", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "type": "simpulseid:Issuer", + "member": "did:web:did.ascs.digital:participants:ascs" + }, + "issuanceDate": "2025-08-06T10:15:22Z", + "expirationDate": "2030-08-05T00:00:00Z", + "credentialSubject": { + "id": "did:web:did.ascs.digital:participants:bmw", + "type": [ + "simpulseid:AscsBaseMembership", + "schema:ProgramMembership" + ], + "memberOf": [ + "did:web:did.ascs.digital:participants:ascs", + "did:web:did.ascs.digital:programs:ascs-base-membership" + ], + "programName": "ASCS e.V. Base Membership", + "hostingOrganization": { + "@id": "did:web:did.ascs.digital:participants:ascs", + "type": [ + "schema:Organization", + "gx:LegalPerson" + ], + "name": "Automotive Solution Center for Simulation e.V." + }, + "memberSince": "2023-01-01", + "termsAndConditions": [ + { + "@id": "ipfs://bafybeihdwdcefgh4dqkjv67uzcmw7oj5thlvxnqxnxb4ji54m72w5foemq4", + "@type": "gx:TermsAndConditions", + "gx:url": { + "@value": "https://media.ascs.digital/terms/base_membership_terms_2025-08-05.pdf", + "@type": "xsd:anyURI" + }, + "gx:hash": "bafybeihdwdcefgh4dqkjv67uzcmw7oj5thlvxnqxnxb4ji54m72w5foemq4" + } + ] + }, + "credentialStatus": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#6239c5abac53d33fff4a9babaaae70f6c71ac495cface74d26ac1e3affee8c61", + "type": "harbour:CRSetEntry", + "statusPurpose": "revocation" + } + ] +} diff --git a/examples/simpulseid-ascs-envited-membership-credential.json b/examples/simpulseid-ascs-envited-membership-credential.json new file mode 100644 index 0000000..dd60866 --- /dev/null +++ b/examples/simpulseid-ascs-envited-membership-credential.json @@ -0,0 +1,61 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schema.ascs.digital/SimpulseId/v1/credentials#", + "https://schema.reachhaven.com/Harbour/v1/credentials#" + ], + "type": [ + "VerifiableCredential", + "harbour:VerifiableCredential", + "simpulseid:AscsEnvitedMembershipCredential" + ], + "id": "urn:uuid:8e3a0c1b-4d4e-4f9a-9b0c-1d2e3f4a5b6c", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "type": "simpulseid:Issuer", + "member": "did:web:did.ascs.digital:participants:ascs" + }, + "issuanceDate": "2025-08-06T10:15:22Z", + "expirationDate": "2030-08-05T00:00:00Z", + "credentialSubject": { + "id": "did:web:did.ascs.digital:participants:bmw", + "type": [ + "simpulseid:AscsEnvitedMembership", + "schema:ProgramMembership" + ], + "memberOf": [ + "did:web:did.ascs.digital:participants:ascs", + "did:web:did.ascs.digital:programs:ascs-base-membership", + "did:web:did.ascs.digital:programs:ascs-envited-membership" + ], + "programName": "ASCS e.V. ENVITED Membership", + "hostingOrganization": { + "@id": "did:web:did.ascs.digital:participants:ascs", + "type": [ + "schema:Organization", + "gx:LegalPerson" + ], + "name": "Automotive Solution Center for Simulation e.V." + }, + "memberSince": "2023-01-01", + "baseMembershipCredential": "urn:uuid:7f3f7c6a-4b4d-4e9e-8f0a-9b1b2c3d4e5f", + "termsAndConditions": [ + { + "@id": "ipfs://bafybeifx7yeb55armcsxwwitkymga5xf53dxiarykms3ygqic7jc6he43m", + "@type": "gx:TermsAndConditions", + "gx:url": { + "@value": "https://media.ascs.digital/terms/envited_membership_terms_2025-08-05.pdf", + "@type": "xsd:anyURI" + }, + "gx:hash": "bafybeifx7yeb55armcsxwwitkymga5xf53dxiarykms3ygqic7jc6he43m" + } + ] + }, + "credentialStatus": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#b8ca800e6cf1807ed35c682ca7c84f07df55ad53a20784fe0ee896f279a6a047", + "type": "harbour:CRSetEntry", + "statusPurpose": "revocation" + } + ] +} diff --git a/examples/simpulseid-participant-credential.json b/examples/simpulseid-participant-credential.json new file mode 100644 index 0000000..39160ab --- /dev/null +++ b/examples/simpulseid-participant-credential.json @@ -0,0 +1,72 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schema.ascs.digital/SimpulseId/v1/credentials#", + "https://schema.reachhaven.com/Harbour/v1/credentials#" + ], + "type": [ + "VerifiableCredential", + "harbour:VerifiableCredential", + "simpulseid:ParticipantCredential" + ], + "id": "urn:uuid:576fbefb-35e8-4b71-bb1a-53d1803c86de", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "type": "simpulseid:Issuer", + "member": "did:web:did.ascs.digital:participants:ascs" + }, + "issuanceDate": "2025-08-06T10:15:22Z", + "expirationDate": "2030-08-05T00:00:00Z", + "credentialSubject": { + "id": "did:web:did.ascs.digital:participants:bmw", + "type": [ + "simpulseid:Participant", + "gx:LegalPerson", + "schema:Organization" + ], + "legalName": "Bayerische Motoren Werke Aktiengesellschaft", + "legalForm": "AG", + "registrationNumber": { + "@type": "gx:VatID", + "countryCode": "DE", + "vatID": "DE129273398" + }, + "duns": "313995269", + "email": "imprint@bmw.com", + "website": "https://www.bmwgroup.com/", + "legalAddress": { + "@type": "gx:Address", + "streetAddress": "Petuelring 130", + "postalCode": "80809", + "locality": "München", + "countryCode": "DE", + "region": "DE21" + }, + "headquartersAddress": { + "@type": "gx:Address", + "streetAddress": "Petuelring 130", + "postalCode": "80809", + "locality": "München", + "countryCode": "DE", + "region": "DE21" + }, + "termsAndConditions": [ + { + "@id": "ipfs://bafybeigdyj2p5rxbqzqx3g2m5qw4ftheq7l3z5v7j7nw2q7q5q5zq7q5q4", + "@type": "gx:TermsAndConditions", + "gx:url": { + "@value": "https://media.ascs.digital/terms/simpulse_id_terms_2025-08-05.pdf", + "@type": "xsd:anyURI" + }, + "gx:hash": "bafybeigdyj2p5rxbqzqx3g2m5qw4ftheq7l3z5v7j7nw2q7q5q5zq7q5q4" + } + ] + }, + "credentialStatus": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#608101d3a8430e61f60dcf1be0f42ab3ceb52b6abffb9f75b6f36c80362fc25a", + "type": "harbour:CRSetEntry", + "statusPurpose": "revocation" + } + ] +} diff --git a/examples/simpulseid-user-credential.json b/examples/simpulseid-user-credential.json new file mode 100644 index 0000000..3a11ab4 --- /dev/null +++ b/examples/simpulseid-user-credential.json @@ -0,0 +1,53 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schema.ascs.digital/SimpulseId/v1/credentials#", + "https://schema.reachhaven.com/Harbour/v1/credentials#" + ], + "type": [ + "VerifiableCredential", + "harbour:VerifiableCredential", + "simpulseid:UserCredential" + ], + "id": "urn:uuid:6a0e7e84-2e88-4b9a-977b-9e92a6d87a0f", + "issuer": { + "id": "did:web:did.ascs.digital:participants:bmw", + "type": "simpulseid:Participant", + "member": "did:web:did.ascs.digital:participants:bmw" + }, + "issuanceDate": "2025-08-06T10:15:22Z", + "expirationDate": "2030-08-05T00:00:00Z", + "credentialSubject": { + "id": "did:web:did.ascs.digital:users:44b982bb-ae61-4f6f-899f-a0982aaf367e", + "type": [ + "simpulseid:User", + "gx:NaturalPerson", + "schema:Person" + ], + "memberOf": [ + "did:web:did.ascs.digital:participants:bmw", + "did:web:did.ascs.digital:programs:simpulseid-user" + ], + "givenName": "Max", + "familyName": "Mustermann", + "email": "max.mustermann@bmw.com", + "termsAndConditions": [ + { + "@id": "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq", + "@type": "gx:TermsAndConditions", + "gx:url": { + "@value": "https://media.ascs.digital/terms/privacy_policy_2025-08-05.pdf", + "@type": "xsd:anyURI" + }, + "gx:hash": "bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq" + } + ] + }, + "credentialStatus": [ + { + "id": "did:web:did.ascs.digital:services:revocation-registry#9396f1d42a2a5eaa93a1a3211e4b0db85c8185d533b835984cd98d24ecba6440", + "type": "harbour:CRSetEntry", + "statusPurpose": "revocation" + } + ] +} diff --git a/examples/user-credential-full.json b/examples/user-credential-full.json deleted file mode 100644 index 574d2e4..0000000 --- a/examples/user-credential-full.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - { - "@version": 1.1, - "@protected": true, - "AscsUserCredential": "https://schema.ascs.digital/AscsUserCredential/v1#", - "AscsIssuer": { - "@id": "https://schema.ascs.digital/AscsUserCredential/v1#AscsIssuer", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "url": "https://schema.org/url" - } - }, - "AscsUser": { - "@id": "https://schema.ascs.digital/AscsUserCredential/v1#AscsUser", - "@context": { - "@version": 1.1, - "@protected": true, - "name": "https://schema.org/name", - "email": "https://schema.org/email", - "address": "http://schema.org/address", - "isAscsMember": "http://schema.org/Boolean", - "isEnvitedMember": "http://schema.org/Boolean", - "privacyPolicy": "https://schema.org/termsOfService" - } - }, - "PostalAddress": { - "@id": "http://schema.org/PostalAddress", - "@context": { - "@version": 1.1, - "@protected": true, - "streetAddress": "http://schema.org/streetAddress", - "postalCode": "http://schema.org/postalCode", - "addressLocality": "http://schema.org/addressLocality", - "addressCountry": "https://schema.org/addressCountry" - } - } - } - ], - "type": [ - "VerifiableCredential", - "AscsUserCredential" - ], - "issuanceDate": "2023-11-22T17:14:33Z", - "expirationDate": "2102-09-15T17:14:33Z", - "id": "urn:uuid:cf1f329d-9c4c-458e-ba0a-a762a296b79c", - "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8", - "type": "AscsIssuer", - "name": "Testcompany GmbH", - "url": "https://test.de/" - }, - "credentialSubject": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1SfdVU1mor3Sgej3FmmwMH4HM1EjTzqqeE", - "type": "AscsUser", - "name": "User", - "email": "mailto:user@test.de", - "address": { - "type": "PostalAddress", - "streetAddress": "Teststraße 1", - "postalCode": "12345", - "addressLocality": "Munich", - "addressCountry": "DE" - }, - "isAscsMember": true, - "isEnvitedMember": true, - "privacyPolicy": "https://media.ascs.digital/terms/ascs_privacy_policy_2020-07-08.pdf#cidv1" - } -} \ No newline at end of file diff --git a/examples/user-credential.json b/examples/user-credential.json deleted file mode 100644 index dfbbc09..0000000 --- a/examples/user-credential.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://schema.ascs.digital/AscsUserCredential/v1" - ], - "type": [ - "VerifiableCredential", - "AscsUserCredential" - ], - "issuanceDate": "2023-11-22T17:14:33Z", - "expirationDate": "2102-09-15T17:14:33Z", - "id": "urn:uuid:cf1f329d-9c4c-458e-ba0a-a762a296b79c", - "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8", - "type": "AscsIssuer", - "name": "Testcompany GmbH", - "url": "https://test.de/" - }, - "credentialSubject": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1SfdVU1mor3Sgej3FmmwMH4HM1EjTzqqeE", - "type": "AscsUser", - "name": "User", - "email": "mailto:user@test.de", - "address": { - "type": "PostalAddress", - "streetAddress": "Teststraße 1", - "postalCode": "12345", - "addressLocality": "Munich", - "addressCountry": "DE" - }, - "isAscsMember": true, - "isEnvitedMember": true, - "privacyPolicy": "https://media.ascs.digital/terms/ascs_privacy_policy_2020-07-08.pdf#cidv1" - } -} \ No newline at end of file diff --git a/generated/README_contexts.md b/generated/README_contexts.md new file mode 100644 index 0000000..1651684 --- /dev/null +++ b/generated/README_contexts.md @@ -0,0 +1,151 @@ +# JSON-LD Contexts for SimpulseID Credentials + +This folder contains the JSON-LD context documents used by SimpulseID +credentials in the ENVITED Ecosystem. These contexts define how JSON properties, +classes, and identifiers in credentials are expanded into stable IRIs and how +they map to the SimpulseID ontology, Gaia-X definitions, schema.org and vCard. + +All contexts in this folder are intended to be hosted under: + +```txt + +``` + +with the following MIME type: + +```txt +Content-Type: application/ld+json +``` + +Hosting contexts in this way ensures: + +- OIDC4VP clients can dereference the contexts reliably +- SSI wallets can resolve schemas consistently +- JSON-LD processors interpret IRIs deterministically + +--- + +## Included Contexts + +### `SimpulseIdCredentials.json` + +The main JSON-LD context. +It defines all SimpulseID-specific classes and properties: + +- Participant (`simpulseid:Participant`) +- ASCS Base Membership (`simpulseid:AscsBaseMembership`) +- ASCS ENVITED Membership (`simpulseid:AscsEnvitedMembership`) +- Administrator (`simpulseid:Administrator`) +- User (`simpulseid:User`) + +It maps JSON fields to: + +- SimpulseID ontology terms +- Gaia-X identity primitives (`gx:LegalPerson`, `gx:Address`, `gx:VatID`, etc.) +- vCard properties for address modelling (`street-address`, `locality`, `region`) +- Legal form vocabulary entries +- Terms & conditions references (`simpulseid:termsAndConditions`) + +The context uses `@protected: true` to guarantee a stable schema. + +--- + +### `HarbourCredentials.json` + +Context for revocation metadata following the Harbour Status model. + +Defines: + +- `harbour:CRSetEntry` +- `statusPurpose` (typed as `xsd:string`) +- `harbour:VerifiableCredential` + +Each SimpulseID credential uses this context in `credentialStatus`. + +--- + +### `legalForm-v1.jsonld` + +A SKOS vocabulary for legal forms, containing entries such as: + +- AG +- GmbH +- LLC +- CIC +- BenCom +- etc. + +Each entry is a `skos:Concept` inside the `simpulseid:LegalForm` concept scheme. + +Credentials reference these IRIs like: + +```txt + +``` + +--- + +## Why Gaia-X Terms Are Used (`gx:*`) + +The Gaia-X Trust Framework provides well-defined, audited semantic identifiers +for legal entities, natural persons, addresses, registration numbers and +terms & conditions. + +SimpulseID reuses these Gaia-X IRIs because: + +1. **Strong interoperability** + Credentials can be reused across Gaia-X–aligned ecosystems and data spaces. + +2. **Regulatory alignment** + Gaia-X definitions follow European governance requirements. + +3. **Semantic completeness** + Classes such as `gx:LegalPerson`, `gx:NaturalPerson`, `gx:Address`, + `gx:VatID` exactly match ENVITED membership needs. + +4. **RDF correctness** + JSON-LD expansion produces a clean RDF graph aligned with the Gaia-X + Credential Format. + +5. **Reusability in EVES** + The ENVITED Ecosystem Specifications intentionally align with Gaia-X, so + reusing these IRIs avoids duplicating definitions. + +--- + +## How These Contexts Work Together + +- `SimpulseIdCredentials.json` + → Defines SimpulseID credential structure + +- `HarbourCredentials.json` + → Adds revocation support fully compatible with VC Data Model v2 + +- `legalForm-v1.jsonld` + → Provides the vocabulary for legal form codes + +- Gaia-X IRIs + → Provide standardized identity semantics + +Together, they allow wallets, OIDC bridges, and verifiers to interpret +SimpulseID credentials correctly. + +--- + +## Relation to Ontologies + +These contexts directly reference terms defined in: + +```txt +/ontologies/SimpulseIdOntology.ttl +``` + +and the legal form code list. + +The ontology defines **semantics**, +the contexts define **how JSON maps to those semantics**. + +--- + +These context documents form the foundation for all credentials issued or +verified by **identity.ascs.digital** and the broader ENVITED Ecosystem. diff --git a/generated/README_ontologies.md b/generated/README_ontologies.md new file mode 100644 index 0000000..3326167 --- /dev/null +++ b/generated/README_ontologies.md @@ -0,0 +1,66 @@ +# SimpulseID Ontologies + +This folder contains the RDF/OWL ontologies that define the formal semantics +for all classes and properties used in SimpulseID credentials. These ontologies +provide the canonical IRIs that are referenced by the JSON-LD contexts in +`../contexts` and interpreted by verifiers, wallets, and backend services. + +## Purpose + +The ontology defines: + +- The **SimpulseID credential model** (Participant, Memberships, User, Admin) +- The **relationships** between these classes +- Alignment with: + - **Gaia‑X Trust Framework** + - **schema.org** + - **vCard** + - **SKOS vocabularies** (e.g., legal form code list) + +By using shared IRIs and clear semantics, SimpulseID ensures interoperability +across the ENVITED Ecosystem and other data spaces. + +## Files + +### `SimpulseIdOntology.ttl` + +Defines: + +- Classes: + + - `simpulseid:Participant` + - `simpulseid:AscsBaseMembership` + - `simpulseid:AscsEnvitedMembership` + - `simpulseid:Administrator` + - `simpulseid:User` + - Program classes for Base and ENVITED memberships + +- Properties: + - `simpulseid:legalForm` + - `simpulseid:termsAndConditions` + - `simpulseid:baseMembership` + - Address fields using Gaia‑X and vCard IRIs + - Organizational membership relations (`schema:memberOf`) + +### Legal Form Vocabulary + +The ontology references the SKOS vocabulary defined in: + +```txt +../contexts/legalForm-v1.jsonld +``` + +This vocabulary provides IRIs for legal forms (AG, GmbH, LLC, CIC, etc.). + +## Why Ontologies Matter + +- JSON-LD contexts define _mapping_ +- Ontologies define _meaning_ +- Together, they create a robust, machine‑interpretable credential model + +These ontologies are essential for: + +- Trust interoperability +- Schema validation +- EVES compliance +- Gaia‑X compatibility diff --git a/linkml/core.yaml b/linkml/core.yaml new file mode 100644 index 0000000..96f7c80 --- /dev/null +++ b/linkml/core.yaml @@ -0,0 +1,30 @@ +id: https://schema.ascs.digital/Core/linkml/v1#Core +name: core +description: > + Shared LinkML core definitions to avoid URI conflicts across imported schemas. + This schema defines common slots once (e.g., id/type) with stable URIs. + JSON-LD keyword aliasing (id -> @id, type -> @type) is applied in the + generation script as a post-processing step. + +prefixes: + linkml: https://w3id.org/linkml/ + xsd: http://www.w3.org/2001/XMLSchema# + +default_prefix: linkml +default_range: string + +imports: + - linkml:types + +slots: + id: + description: Stable identifier slot (post-processed to JSON-LD @id in context output). + slot_uri: linkml:id + required: true + range: uri + + type: + description: Stable type slot (post-processed to JSON-LD @type in context output). + slot_uri: linkml:type + multivalued: true + range: uri diff --git a/linkml/harbour.yaml b/linkml/harbour.yaml new file mode 100644 index 0000000..e6ac635 --- /dev/null +++ b/linkml/harbour.yaml @@ -0,0 +1,78 @@ +id: https://schema.reachhaven.com/Harbour/linkml/v1#Harbour +name: harbour +description: > + LinkML schema for Harbour credentials vocabulary, aligned with the + HarbourCredentials JSON-LD context. + +prefixes: + linkml: https://w3id.org/linkml/ + harbour: https://schema.reachhaven.com/Harbour/v1/credentials# + xsd: http://www.w3.org/2001/XMLSchema# + vcs: https://www.w3.org/ns/credentials/status# + +default_prefix: harbour +default_range: string + +imports: + - linkml:types + - ./core + +slots: + status_purpose: + description: > + Status purpose as defined by VC Status (e.g., "revocation"). + slot_uri: vcs:statusPurpose + range: string + required: false + + endpoint: + description: Service endpoint IRI. + slot_uri: harbour:endpoint + range: uri + required: false + + contract_urn: + description: Contract identifier (URN/IRI) for the revocation registry implementation. + slot_uri: harbour:contractURN + range: uri + required: false + + source_repository: + description: Source repository IRI (e.g., Git repository URL) for the implementation. + slot_uri: harbour:sourceRepository + range: uri + required: false + + implementation: + description: Implementation identifier IRI (e.g., software package or artifact reference). + slot_uri: harbour:implementation + range: uri + required: false + +classes: + HarbourVerifiableCredential: + description: > + Marker class used in Harbour contexts to identify a Verifiable Credential + using the Harbour vocabulary. + class_uri: harbour:VerifiableCredential + + CRSetEntry: + description: > + Credential status list entry (typically used in vc:credentialStatus), + aligned with harbour:CRSetEntry. + class_uri: harbour:CRSetEntry + slots: + - id + - type + - status_purpose + + CRSetServiceEndpoint: + description: > + Service endpoint descriptor for a credential revocation set service. + class_uri: harbour:CRSetServiceEndpoint + slots: + - endpoint + - status_purpose + - contract_urn + - source_repository + - implementation diff --git a/linkml/simpulseid.yaml b/linkml/simpulseid.yaml new file mode 100644 index 0000000..a637b58 --- /dev/null +++ b/linkml/simpulseid.yaml @@ -0,0 +1,529 @@ +id: https://schema.ascs.digital/SimpulseId/linkml/v1#SimpulseId +name: simpulseid +description: > + LinkML schema for SimpulseID credentials, aligned with the SimpulseIdOntology, + SimpulseIdCredentials JSON-LD context, and Gaia-X 24.06 LinkML models. + This schema is intended as the single source of truth to generate JSON-LD + contexts, SHACL shapes, and OWL ontologies for SimpulseID credential types. + +prefixes: + linkml: https://w3id.org/linkml/ + simpulseid: https://schema.ascs.digital/SimpulseId/v1/credentials# + harbour: https://schema.reachhaven.com/Harbour/v1/credentials# + vcs: https://www.w3.org/ns/credentials/status# + gx: https://w3id.org/gaia-x/development# + schema: http://schema.org/ + vc: https://www.w3.org/2018/credentials# + vcard: http://www.w3.org/2006/vcard/ns# + xsd: http://www.w3.org/2001/XMLSchema# + skos: http://www.w3.org/2004/02/skos/core# + lf: https://schema.ascs.digital/SimpulseId/v1/legalForm# + +default_prefix: simpulseid +default_range: string + +imports: + - linkml:types + - ./core + # Single entry point for Gaia-X: pulls in Address, LegalPerson, LegalDocument, + # Issuer, Participant, TermsAndConditions, etc. via gaia-x.yaml. + - ../service-characteristics/linkml/gaia-x + - ./harbour + +slots: + # NOTE: + # id and type are intentionally NOT defined here. + # They must be provided by ./core to avoid merge conflicts. + + issuer: + description: > + Issuer of the Verifiable Credential. This slot uses the VC vocabulary + predicate vc:issuer and points to a SimpulseID-specific Issuer class. + That Issuer class is modelled as a subclass of the imported Gaia-X Issuer + definition to preserve Gaia-X semantics while allowing SimpulseID typing + (simpulseid:Issuer) in the credential payload. + slot_uri: vc:issuer + range: SimpulseIdIssuer + required: true + aliases: [issuer] + + issuance_date: + description: > + Date-time at which the credential was issued. + slot_uri: vc:issuanceDate + range: datetime + required: true + aliases: [issuanceDate] + + expiration_date: + description: > + Date-time at which the credential expires, if any. + slot_uri: vc:expirationDate + range: datetime + required: false + aliases: [expirationDate] + + credential_subject: + description: > + Subject of the Verifiable Credential. + slot_uri: vc:credentialSubject + range: CredentialSubject + required: true + aliases: [credentialSubject] + + credential_status: + description: > + Status entry or entries associated with the Verifiable Credential + (e.g. Harbour CRSet entries). + slot_uri: vc:credentialStatus + range: CredentialStatusEntry + multivalued: true + required: false + aliases: [credentialStatus] + + # Common person-related slots (schema.org) + + given_name: + description: Given name of a natural person. + slot_uri: schema:givenName + range: string + aliases: [givenName] + + family_name: + description: Family name of a natural person. + slot_uri: schema:familyName + range: string + aliases: [familyName] + + email: + description: Email address of a person or organization. + slot_uri: schema:email + range: string + aliases: [email] + + # Membership / program slots (schema.org) + + member_of: + description: > + Membership relation pointing from a person or membership to a participant + or program (DID or IRI). + slot_uri: schema:memberOf + multivalued: true + range: uri + aliases: [memberOf] + + program_name: + description: Human-readable name of the program (e.g. ASCS Base Membership). + slot_uri: schema:programName + range: string + aliases: [programName] + + hosting_organization: + description: > + Organization hosting the membership program (e.g. ASCS e.V. as + hosting organization of ENVITED). + slot_uri: schema:hostingOrganization + range: SimpulseidParticipant + aliases: [hostingOrganization] + + member_since: + description: > + Date on which the membership became effective. + slot_uri: schema:memberSince + range: date + aliases: [memberSince] + + # Address (Gaia-X aligned) – pretty keys via aliases + + address: + description: > + Address of a natural person. This slot is aligned with Gaia-X by using + gx:address as predicate and a SimpulseID Address class that subclasses + the imported Gaia-X Address definition. Individual address fields use + vCard predicates and Gaia-X predicates, while JSON keys remain wallet- + friendly (streetAddress, postalCode, locality, countryCode, region). + slot_uri: gx:address + range: SimpulseIdAddress + required: false + aliases: [address] + + street_address: + description: > + Street address, including street name and house number. Mapped to + vcard:street-address while preserving the JSON key streetAddress. + slot_uri: vcard:street-address + range: string + aliases: [streetAddress] + + postal_code: + description: > + Postal code / ZIP code. Mapped to vcard:postal-code while preserving + the JSON key postalCode. + slot_uri: vcard:postal-code + range: string + aliases: [postalCode] + + locality: + description: > + Locality / city. Mapped to vcard:locality. + slot_uri: vcard:locality + range: string + aliases: [locality] + + country_code: + description: > + ISO 3166-1 alpha-2 country code as required by Gaia-X (gx:countryCode). + Preserves the JSON key countryCode. + slot_uri: gx:countryCode + range: string + required: true + aliases: [countryCode] + + region: + description: > + Region code, typically NUTS2 (e.g. DE21 for Oberbayern) as used in Gaia-X + (gx:region). Preserves the JSON key region. + slot_uri: gx:region + range: string + required: false + aliases: [region] + + # Organization / participant related slots (Gaia-X-aligned) + + legal_name: + description: > + Full legal name of the organization (e.g. Bayerische Motoren Werke + Aktiengesellschaft). Uses the Gaia-X legalName property. + slot_uri: gx:legalName + range: string + aliases: [legalName] + + legal_form: + description: > + Legal form of a legal person using the Gaia-X property gx:legalForm. + Values are IRIs from the SimpulseID legal form vocabulary (lf:*), + modelled directly in this LinkML schema (no standalone JSON-LD file). + JSON key preserved as legalForm. + slot_uri: gx:legalForm + range: SimpulseIdLegalForm + required: false + aliases: [legalForm] + + legal_address: + description: > + Legal address of the organization, as required by the Gaia-X Trust + Framework (gx:legalAddress). + slot_uri: gx:legalAddress + range: SimpulseIdAddress + required: true + aliases: [legalAddress] + + headquarters_address: + description: > + Headquarters address of the organization, optional according to the + Gaia-X Trust Framework (gx:headquartersAddress). + slot_uri: gx:headquartersAddress + range: SimpulseIdAddress + required: false + aliases: [headquartersAddress] + + # Terms and Conditions / agreements (Gaia-X LegalDocument) + + terms_and_conditions: + description: > + References to legal documents representing Terms and Conditions records. + Implemented using Gaia-X LegalDocument as range and gx:termsAndConditions + as the property IRI. JSON key preserved as termsAndConditions. + slot_uri: gx:termsAndConditions + range: LegalDocument + multivalued: true + aliases: [termsAndConditions] + + # Base membership credential cross-reference + + base_membership_credential: + description: > + Reference from an ENVITED membership subject to its underlying base + membership credential (URN of the credential). + slot_uri: simpulseid:baseMembershipCredential + range: uri + aliases: [baseMembershipCredential] + +enums: + SimpulseIdLegalForm: + description: > + SimpulseID legal form vocabulary modelled as a controlled code list. + Each value is expressed as an IRI in the lf: namespace. This replaces the + previously standalone JSON-LD vocabulary file while keeping stable, + dereferenceable identifiers for legal forms. + permissible_values: + LLC: + description: Limited Liability Company + meaning: lf:LLC + Corporation: + description: Corporation + meaning: lf:Corporation + LimitedPartnership: + description: Limited Partnership + meaning: lf:LimitedPartnership + NonprofitCorporation: + description: Nonprofit Corporation + meaning: lf:NonprofitCorporation + GmbH: + description: Gesellschaft mit beschränkter Haftung / Limited liability company (GmbH) + meaning: lf:GmbH + AG: + description: Aktiengesellschaft / Stock corporation (AG) + meaning: lf:AG + Einzelunternehmen: + description: Einzelunternehmen / Sole proprietorship + meaning: lf:Einzelunternehmen + GbR: + description: Gesellschaft bürgerlichen Rechts / Civil-law partnership (GbR) + meaning: lf:GbR + OHG: + description: Offene Handelsgesellschaft / General partnership (OHG) + meaning: lf:OHG + KG: + description: Kommanditgesellschaft / Limited partnership (KG) + meaning: lf:KG + UG: + description: Unternehmergesellschaft (haftungsbeschränkt) / Entrepreneurial company (UG) + meaning: lf:UG + SoleTrader: + description: Sole Trader + meaning: lf:SoleTrader + UnincorporatedAssociation: + description: Unincorporated Association + meaning: lf:UnincorporatedAssociation + Partnership: + description: Partnership + meaning: lf:Partnership + Trust: + description: Trust + meaning: lf:Trust + LimitedCompany: + description: Limited Company + meaning: lf:LimitedCompany + LLP: + description: Limited Liability Partnership + meaning: lf:LLP + CIC: + description: Community Interest Company (CIC) + meaning: lf:CIC + CIO: + description: Charitable Incorporated Organisation (CIO) + meaning: lf:CIO + CooperativeSociety: + description: Co-operative Society + meaning: lf:CooperativeSociety + BenCom: + description: Community Benefit Society (BenCom) + meaning: lf:BenCom + other: + description: Other / unspecified legal form + meaning: lf:other + +classes: + ############################################# + # Option A: Gaia-X reuse + SimpulseID subclasses + ############################################# + + SimpulseIdIssuer: + description: > + SimpulseID issuer class used in VC instances (simpulseid:Issuer). + This class is a subclass of the imported Gaia-X Issuer definition, + enabling alignment with Gaia-X semantics while allowing SimpulseID-specific + typing in credentials. In JSON, the issuer object can therefore use + "type": "simpulseid:Issuer". + is_a: Issuer + class_uri: simpulseid:Issuer + + SimpulseIdAddress: + description: > + Address class for SimpulseID credentials. It subclasses the imported + Gaia-X Address definition and uses gx:Address as RDF class. Address + fields are mapped to Gaia-X/vCard predicates through slot_uri, while the + JSON keys remain wallet-friendly through LinkML aliases. + is_a: Address + class_uri: gx:Address + slots: + - street_address + - postal_code + - locality + - country_code + - region + + ############################################# + # Core abstractions + ############################################# + + CredentialSubject: + description: > + Abstract base class for all SimpulseID credential subject entities + (participants, memberships, users, administrators). + abstract: true + class_uri: schema:Thing + slots: + - id + - type + - terms_and_conditions + + VerifiableCredential: + description: > + Base class for SimpulseID Verifiable Credentials. + class_uri: vc:VerifiableCredential + slots: + - id + - type + - issuer + - issuance_date + - expiration_date + - credential_subject + - credential_status + + CredentialStatusEntry: + description: > + Status entry describing revocation state, typically a Harbour + CRSetEntry. + class_uri: schema:Thing + slots: + - id + - type + # Harbour-specific properties stay in Harbour’s own context/ontology. + + ############################################# + # Participant / person classes + ############################################# + + SimpulseidParticipant: + description: > + SimpulseID Participant entity representing a legal person taking part + in SimpulseID programs. It specializes the Gaia-X LegalPerson and adds + SimpulseID-specific properties such as gx:legalName, gx:legalForm and + gx:termsAndConditions. + is_a: LegalPerson + mixins: + - CredentialSubject + class_uri: simpulseid:Participant + slots: + - legal_name + - legal_form + - legal_address + - headquarters_address + - email + - terms_and_conditions + # Note: registrationNumber comes from Gaia-X LegalPerson and remains + # the identifier for this class according to Gaia-X. + + User: + description: > + SimpulseID User entity, representing a natural person acting in the + ENVITED Ecosystem. + is_a: CredentialSubject + class_uri: simpulseid:User + slots: + - given_name + - family_name + - email + - member_of + - address + + Administrator: + description: > + SimpulseID Administrator entity, representing a natural person with + elevated rights in the ENVITED Ecosystem. + is_a: CredentialSubject + class_uri: simpulseid:Administrator + slots: + - given_name + - family_name + - email + - member_of + - address + + AscsBaseMembership: + description: > + ASCS Base Membership entity, representing the base membership of a + participant in ASCS e.V. + is_a: CredentialSubject + class_uri: simpulseid:AscsBaseMembership + slots: + - program_name + - hosting_organization + - member_of + - member_since + + AscsEnvitedMembership: + description: > + ASCS ENVITED Membership entity, representing membership of a participant + in the ENVITED program, requiring an underlying base membership credential. + is_a: CredentialSubject + class_uri: simpulseid:AscsEnvitedMembership + slots: + - program_name + - hosting_organization + - member_of + - member_since + - base_membership_credential + + ############################################# + # Credential classes + ############################################# + + ParticipantCredential: + description: > + SimpulseID Participant Credential. Verifiable Credential asserting that + the subject is a SimpulseID Participant (a Gaia-X LegalPerson in the + context of the ENVITED Ecosystem). + is_a: VerifiableCredential + class_uri: simpulseid:ParticipantCredential + slot_usage: + credential_subject: + range: SimpulseidParticipant + required: true + + AscsBaseMembershipCredential: + description: > + SimpulseID ASCS Base Membership Credential. Verifiable Credential asserting + base membership of a participant in ASCS e.V. + is_a: VerifiableCredential + class_uri: simpulseid:AscsBaseMembershipCredential + slot_usage: + credential_subject: + range: AscsBaseMembership + required: true + + AscsEnvitedMembershipCredential: + description: > + SimpulseID ASCS ENVITED Membership Credential. Verifiable Credential + asserting ENVITED membership status, referencing an underlying base + membership credential. + is_a: VerifiableCredential + class_uri: simpulseid:AscsEnvitedMembershipCredential + slot_usage: + credential_subject: + range: AscsEnvitedMembership + required: true + + AdministratorCredential: + description: > + SimpulseID Administrator Credential. Verifiable Credential asserting that + the subject is an administrator. + is_a: VerifiableCredential + class_uri: simpulseid:AdministratorCredential + slot_usage: + credential_subject: + range: Administrator + required: true + + UserCredential: + description: > + SimpulseID User Credential. Verifiable Credential asserting that the + subject is a SimpulseID User. + is_a: VerifiableCredential + class_uri: simpulseid:UserCredential + slot_usage: + credential_subject: + range: User + required: true diff --git a/manifests/README.md b/manifests/README.md new file mode 100644 index 0000000..a7645f8 --- /dev/null +++ b/manifests/README.md @@ -0,0 +1,58 @@ +# Wallet Rendering Manifests for SimpulseID Credentials + +This folder contains **wallet rendering manifests** for each credential type. +These files follow the **Decentralized Identity Foundation (DIF) Wallet Rendering** +specification and define how SSI wallets (such as Altme) should display the +credential to end users. + +## Purpose + +Manifests control: + +- Title and subtitle displayed on the credential card +- Key fields shown on the card (e.g., name, organization, membership type) +- How credentials appear in credential lists and detail views +- Icons, colors, and layout options (if supported by the wallet) + +All manifest files correspond 1:1 to credential types found in: + +```txt +../examples/ +``` + +and are intended for use by wallets integrated with: + +**** + +## Files + +### `SimpulseIdParticipantCredentialManifest.json` + +Defines card rendering for Participant Credentials (gx:LegalPerson). + +### `SimpulseIdAscsBaseMembershipCredentialManifest.json` + +Defines rendering for ASCS Base Membership credentials. + +### `SimpulseIdAscsEnvitedMembershipCredentialManifest.json` + +Rendering for ENVITED Program Membership credentials. + +### `SimpulseIdAdministratorCredentialManifest.json` + +Rendering of administrator credentials issued by ASCS. + +### `SimpulseIdUserCredentialManifest.json` + +Rendering of user credentials issued by participant administrators. + +## Usage + +Wallets use these manifests to visually render credentials when they are: + +- Added to the wallet +- Displayed in a card list +- Displayed in detail view +- Offered for presentation via OIDC4VP + +They do **not** affect cryptographic validity—only **UX rendering**. diff --git a/manifests/SimpulseIdAdministratorCredentialManifest.json b/manifests/SimpulseIdAdministratorCredentialManifest.json new file mode 100644 index 0000000..97a1628 --- /dev/null +++ b/manifests/SimpulseIdAdministratorCredentialManifest.json @@ -0,0 +1,181 @@ +{ + "id": "SimpulseIdAdministratorManifest", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "name": "Automotive Solution Center for Simulation e.V." + }, + "output_descriptors": [ + { + "id": "https://schema.ascs.digital/SimpulseId/v1/credentials#AdministratorManifest", + "schema": "https://schema.ascs.digital/SimpulseId/v1/credentials#Administrator", + "styles": { + "background": { + "color": "#60ac24" + }, + "text": { + "color": "#ffffff" + } + }, + "display": { + "title": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "BMW Administrator Credential" + }, + "subtitle": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "Automotive Solution Center for Simulation e.V." + }, + "description": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "This credential certifies the administrator's role at BMW, granting specific permissions and responsibilities." + }, + "properties": [ + { + "path": [ + "$.credentialSubject.givenName", + "$.credentialSubject.familyName" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Full Name" + }, + { + "path": [ + "$.credentialSubject.email" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Email Address" + }, + { + "path": [ + "$.credentialSubject.memberOf" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Organization" + }, + { + "path": [ + "$.credentialSubject.address.streetAddress" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Street Address" + }, + { + "path": [ + "$.credentialSubject.address.postalCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Postal Code" + }, + { + "path": [ + "$.credentialSubject.address.addressLocality" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "City" + }, + { + "path": [ + "$.credentialSubject.address.countrySubdivisionCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "State" + }, + { + "path": [ + "$.credentialSubject.address.country" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Country" + }, + { + "path": [ + "$.credentialSubject.termsAndConditions[0].@id" + ], + "schema": { + "type": "string", + "format": "uri" + }, + "fallback": "Unknown", + "label": "Terms & Conditions" + }, + { + "path": [ + "$.issuanceDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Issued On" + }, + { + "path": [ + "$.expirationDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Expires On" + } + ] + } + } + ], + "presentation_definition": { + "input_descriptors": [ + { + "constraints": { + "fields": [ + { + "filter": { + "pattern": "TezosAssociatedAddress", + "type": "string" + }, + "path": [ + "$.type" + ] + } + ] + }, + "id": "f2a7402b-f649-11ed-834e-0a1628958560", + "purpose": "Select a Tezos address for verification" + } + ] + } +} diff --git a/manifests/AscsMemberCredentialManifest.json b/manifests/SimpulseIdAscsBaseMembershipCredentialManifest.json similarity index 63% rename from manifests/AscsMemberCredentialManifest.json rename to manifests/SimpulseIdAscsBaseMembershipCredentialManifest.json index b9156e7..1d263a8 100644 --- a/manifests/AscsMemberCredentialManifest.json +++ b/manifests/SimpulseIdAscsBaseMembershipCredentialManifest.json @@ -1,16 +1,16 @@ { - "id": "AscsMemberCredentialManifest", + "id": "SimpulseIdAscsBaseMembershipManifest", "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1ggujjYjA7oYoaZBzTg1tYSXn3VMjcgDuv", + "id": "did:web:did.ascs.digital:participants:ascs", "name": "Automotive Solution Center for Simulation e.V." }, "output_descriptors": [ { - "id": "https://schema.ascs.digital/AscsMemberCredential/v1#Manifest", - "schema": "AscsMemberCredential", + "id": "https://schema.ascs.digital/SimpulseId/v1/credentials#AscsBaseMembershipManifest", + "schema": "https://schema.ascs.digital/SimpulseId/v1/credentials#AscsBaseMembership", "styles": { "background": { - "color": "#60ac24" + "color": "#0066cc" }, "text": { "color": "#ffffff" @@ -22,7 +22,7 @@ "schema": { "type": "string" }, - "fallback": "asc(s Member Credential" + "fallback": "ASCS Base Membership" }, "subtitle": { "path": [], @@ -36,103 +36,102 @@ "schema": { "type": "string" }, - "fallback": "A certificate that attests the organizations membership in the asc(s and optionally the ENVITED research cluster confering the mandate to perform a number of actions on behalf of that organisation." + "fallback": "This credential certifies the base membership in ASCS e.V., granting access to core services and benefits." }, "properties": [ { "path": [ - "$.credentialSubject.name" + "$.credentialSubject.programName" ], "schema": { - "type": "text" + "type": "string" }, "fallback": "Unknown", - "label": "Organization name" + "label": "Membership Program" }, { "path": [ - "$.credentialSubject.address.addressCountry" + "$.credentialSubject.hostingOrganization.name" ], "schema": { - "type": "text" + "type": "string" }, "fallback": "Unknown", - "label": "Country" + "label": "Hosting Organization" }, { "path": [ - "$.credentialSubject.isAscsMember" + "$.credentialSubject.member" ], "schema": { - "type": "boolean" + "type": "string" }, "fallback": "Unknown", - "label": "asc(s membership" + "label": "Member" }, { "path": [ - "$.credentialSubject.isEnvitedMember" + "$.credentialSubject.memberOf[0]" ], "schema": { - "type": "boolean" + "type": "string" }, "fallback": "Unknown", - "label": "ENVITED membership" + "label": "Member Of (Organization)" }, { "path": [ - "$.credentialSubject.privacyPolicy" + "$.credentialSubject.memberOf[1]" ], "schema": { - "type": "string", - "format": "uri" + "type": "string" }, "fallback": "Unknown", - "label": "Privacy policy" + "label": "Member Of (Program)" }, { "path": [ - "$.credentialSubject.articlesOfAssociation" + "$.credentialSubject.memberSince" ], "schema": { "type": "string", - "format": "uri" + "format": "date" }, "fallback": "Unknown", - "label": "Articles of association" + "label": "Member Since" }, { "path": [ - "$.credentialSubject.contributionRules" + "$.credentialSubject.termsAndConditions[0].@id" ], "schema": { "type": "string", "format": "uri" }, "fallback": "Unknown", - "label": "Contribution rules" + "label": "Terms & Conditions" }, { "path": [ "$.issuanceDate" ], "schema": { - "type": "text", + "type": "string", "format": "date" }, "fallback": "Unknown", - "label": "Issue date" + "label": "Issued On" }, { "path": [ "$.expirationDate" ], "schema": { - "type": "text", + "type": "string", "format": "date" }, "fallback": "Unknown", - "label": "Expiration date" + "label": "Expires On" } ] } @@ -155,8 +154,8 @@ ] }, "id": "f2a7402b-f649-11ed-834e-0a1628958560", - "purpose": "Select a Tezos address" + "purpose": "Select a Tezos address for verification" } ] } -} \ No newline at end of file +} diff --git a/manifests/SimpulseIdAscsEnvitedMembershipCredentialManifest.json b/manifests/SimpulseIdAscsEnvitedMembershipCredentialManifest.json new file mode 100644 index 0000000..18b91dc --- /dev/null +++ b/manifests/SimpulseIdAscsEnvitedMembershipCredentialManifest.json @@ -0,0 +1,181 @@ +{ + "id": "SimpulseIdAscsEnvitedMembershipManifest", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "name": "Automotive Solution Center for Simulation e.V." + }, + "output_descriptors": [ + { + "id": "https://schema.ascs.digital/SimpulseId/v1/credentials#AscsEnvitedMembershipManifest", + "schema": "https://schema.ascs.digital/SimpulseId/v1/credentials#AscsEnvitedMembership", + "styles": { + "background": { + "color": "#008080" + }, + "text": { + "color": "#ffffff" + } + }, + "display": { + "title": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "ASCS ENVITED Membership" + }, + "subtitle": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "Automotive Solution Center for Simulation e.V." + }, + "description": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "This credential certifies the ENVITED membership in ASCS e.V., granting access to advanced services and collaboration tools." + }, + "properties": [ + { + "path": [ + "$.credentialSubject.programName" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Membership Program" + }, + { + "path": [ + "$.credentialSubject.hostingOrganization.name" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Hosting Organization" + }, + { + "path": [ + "$.credentialSubject.member" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Member" + }, + { + "path": [ + "$.credentialSubject.memberOf[0]" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Member Of (Organization)" + }, + { + "path": [ + "$.credentialSubject.memberOf[1]" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Member Of (Base Program)" + }, + { + "path": [ + "$.credentialSubject.memberOf[2]" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Member Of (ENVITED Program)" + }, + { + "path": [ + "$.credentialSubject.memberSince" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Member Since" + }, + { + "path": [ + "$.credentialSubject.baseMembership" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Base Membership ID" + }, + { + "path": [ + "$.credentialSubject.termsAndConditions[0].@id" + ], + "schema": { + "type": "string", + "format": "uri" + }, + "fallback": "Unknown", + "label": "Terms & Conditions" + }, + { + "path": [ + "$.issuanceDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Issued On" + }, + { + "path": [ + "$.expirationDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Expires On" + } + ] + } + } + ], + "presentation_definition": { + "input_descriptors": [ + { + "constraints": { + "fields": [ + { + "filter": { + "pattern": "TezosAssociatedAddress", + "type": "string" + }, + "path": [ + "$.type" + ] + } + ] + }, + "id": "f2a7402b-f649-11ed-834e-0a1628958560", + "purpose": "Select a Tezos address for verification" + } + ] + } +} diff --git a/manifests/SimpulseIdParticipantCredentialManifest.json b/manifests/SimpulseIdParticipantCredentialManifest.json new file mode 100644 index 0000000..882c966 --- /dev/null +++ b/manifests/SimpulseIdParticipantCredentialManifest.json @@ -0,0 +1,261 @@ +{ + "id": "SimpulseIdParticipantManifest", + "issuer": { + "id": "did:web:did.ascs.digital:participants:ascs", + "name": "Automotive Solution Center for Simulation e.V." + }, + "output_descriptors": [ + { + "id": "https://schema.ascs.digital/SimpulseId/v1/credentials#ParticipantManifest", + "schema": "https://schema.ascs.digital/SimpulseId/v1/credentials#Participant", + "styles": { + "background": { + "color": "#003366" + }, + "text": { + "color": "#ffffff" + } + }, + "display": { + "title": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "Company Participant Credential" + }, + "subtitle": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "Automotive Solution Center for Simulation e.V." + }, + "description": { + "path": [], + "schema": { + "type": "string" + }, + "fallback": "This credential certifies the company's participation in the ASCS ecosystem, enabling access to collaborative services and resources." + }, + "properties": [ + { + "path": [ + "$.credentialSubject.legalName" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Company Name" + }, + { + "path": [ + "$.credentialSubject.legalForm" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Form" + }, + { + "path": [ + "$.credentialSubject.registrationNumber.vatID" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "VAT ID" + }, + { + "path": [ + "$.credentialSubject.duns" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "DUNS Number" + }, + { + "path": [ + "$.credentialSubject.email" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Email" + }, + { + "path": [ + "$.credentialSubject.website" + ], + "schema": { + "type": "string", + "format": "uri" + }, + "fallback": "Unknown", + "label": "Website" + }, + { + "path": [ + "$.credentialSubject.legalAddress.streetAddress" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Address (Street)" + }, + { + "path": [ + "$.credentialSubject.legalAddress.postalCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Address (Postal Code)" + }, + { + "path": [ + "$.credentialSubject.legalAddress.addressLocality" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Address (City)" + }, + { + "path": [ + "$.credentialSubject.legalAddress.countrySubdivisionCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Address (State)" + }, + { + "path": [ + "$.credentialSubject.legalAddress.country" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Legal Address (Country)" + }, + { + "path": [ + "$.credentialSubject.headquartersAddress.streetAddress" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Headquarters (Street)" + }, + { + "path": [ + "$.credentialSubject.headquartersAddress.postalCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Headquarters (Postal Code)" + }, + { + "path": [ + "$.credentialSubject.headquartersAddress.addressLocality" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Headquarters (City)" + }, + { + "path": [ + "$.credentialSubject.headquartersAddress.countrySubdivisionCode" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Headquarters (State)" + }, + { + "path": [ + "$.credentialSubject.headquartersAddress.country" + ], + "schema": { + "type": "string" + }, + "fallback": "Unknown", + "label": "Headquarters (Country)" + }, + { + "path": [ + "$.credentialSubject.termsAndConditions[0].@id" + ], + "schema": { + "type": "string", + "format": "uri" + }, + "fallback": "Unknown", + "label": "Terms & Conditions" + }, + { + "path": [ + "$.issuanceDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Issued On" + }, + { + "path": [ + "$.expirationDate" + ], + "schema": { + "type": "string", + "format": "date" + }, + "fallback": "Unknown", + "label": "Expires On" + } + ] + } + } + ], + "presentation_definition": { + "input_descriptors": [ + { + "constraints": { + "fields": [ + { + "filter": { + "pattern": "TezosAssociatedAddress", + "type": "string" + }, + "path": [ + "$.type" + ] + } + ] + }, + "id": "f2a7402b-f649-11ed-834e-0a1628958560", + "purpose": "Select a Tezos address for verification" + } + ] + } +} diff --git a/manifests/AscsUserCredentialManifest.json b/manifests/SimpulseIdUserCredentialManifest.json similarity index 55% rename from manifests/AscsUserCredentialManifest.json rename to manifests/SimpulseIdUserCredentialManifest.json index dc80ccc..bf56b8d 100644 --- a/manifests/AscsUserCredentialManifest.json +++ b/manifests/SimpulseIdUserCredentialManifest.json @@ -1,13 +1,13 @@ { - "id": "AscsUserCredentialManifest", + "id": "SimpulseIdUserManifest", "issuer": { - "id": "did:pkh:tezos:NetXnHfVqm9iesp:tz1bpeJArd7apJyTUryfXH1SD6w8GL6Gwhj8", - "name": "Testcompany GmbH" + "id": "did:web:did.ascs.digital:participants:bmw", + "name": "Bayerische Motoren Werke Aktiengesellschaft" }, "output_descriptors": [ { - "id": "https://schema.ascs.digital/AscsUserCredential/v1#Manifest", - "schema": "AscsUserCredential", + "id": "https://schema.ascs.digital/SimpulseId/v1/credentials#UserManifest", + "schema": "https://schema.ascs.digital/SimpulseId/v1/credentials#User", "styles": { "background": { "color": "#7891bb" @@ -22,105 +22,86 @@ "schema": { "type": "string" }, - "fallback": "asc(s User Credential" + "fallback": "BMW User Credential" }, "subtitle": { "path": [], "schema": { "type": "string" }, - "fallback": "Automotive Solution Center for Simulation e.V." + "fallback": "Bayerische Motoren Werke AG" }, "description": { "path": [], "schema": { "type": "string" }, - "fallback": "A certificate that attests the users affiliation with an organization holding a membership in the asc(s and optionally the ENVITED research cluster confering the mandate to access services within the ecosystem." + "fallback": "This credential certifies the user's affiliation with BMW, granting access to ecosystem services." }, "properties": [ { "path": [ - "$.credentialSubject.name" + "$.credentialSubject.givenName", + "$.credentialSubject.familyName" ], "schema": { - "type": "text" + "type": "string" }, "fallback": "Unknown", - "label": "User name" - }, - { - "path": [ - "$.credentialSubject.address.addressCountry" - ], - "schema": { - "type": "text" - }, - "fallback": "Unknown", - "label": "Country" + "label": "Full Name" }, { "path": [ "$.credentialSubject.email" ], "schema": { - "type": "text" - }, - "fallback": "Unknown", - "label": "E-Mail" - }, - { - "path": [ - "$.credentialSubject.isAscsMember" - ], - "schema": { - "type": "boolean" + "type": "string" }, "fallback": "Unknown", - "label": "asc(s membership" + "label": "Email Address" }, { "path": [ - "$.credentialSubject.isEnvitedMember" + "$.credentialSubject.memberOf" ], "schema": { - "type": "boolean" + "type": "string" }, "fallback": "Unknown", - "label": "ENVITED membership" + "label": "Organization" }, { "path": [ - "$.credentialSubject.privacyPolicy" + "$.credentialSubject.termsAndConditions[0].@id" ], "schema": { "type": "string", "format": "uri" }, "fallback": "Unknown", - "label": "Privacy policy" + "label": "Terms & Conditions" }, { "path": [ "$.issuanceDate" ], "schema": { - "type": "text", + "type": "string", "format": "date" }, "fallback": "Unknown", - "label": "Issue date" + "label": "Issued On" }, { "path": [ "$.expirationDate" ], "schema": { - "type": "text", + "type": "string", "format": "date" }, "fallback": "Unknown", - "label": "Expiration date" + "label": "Expires On" } ] } @@ -143,8 +124,8 @@ ] }, "id": "f2a7402b-f649-11ed-834e-0a1628958560", - "purpose": "Select a Tezos address" + "purpose": "Select a Tezos address for verification" } ] } -} \ No newline at end of file +} diff --git a/ontology-management-base b/ontology-management-base new file mode 160000 index 0000000..6d957bd --- /dev/null +++ b/ontology-management-base @@ -0,0 +1 @@ +Subproject commit 6d957bd0b98bc094c2eb835682b9ae44af064a01 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e8d8ea9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,16 @@ +# Core LinkML libraries (generators, runtime, datamodel) +linkml==1.9.6 +linkml-runtime==1.9.5 + +# RDF & SHACL stack needed by LinkML and your existing scripts +rdflib==7.5.0 +pyshacl==0.30.1 + +# JSON-LD parsing and framing +pyld==2.0.4 + +# YAML loader for LinkML schema parsing +PyYAML==6.0.3 + +# General utilities used in your scripts +click==8.1.8 diff --git a/service-characteristics b/service-characteristics new file mode 160000 index 0000000..702940d --- /dev/null +++ b/service-characteristics @@ -0,0 +1 @@ +Subproject commit 702940d9b20b24d89057394dfd333c06d5c9a73a diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..6b7884d --- /dev/null +++ b/src/README.md @@ -0,0 +1,53 @@ +# Generated artefacts + +This folder contains downstream artefacts generated from LinkML schemas: + +- JSON-LD context (`*_context.jsonld`) +- SHACL shapes (`*_shacl.ttl`) +- OWL ontology (`*_ontology.ttl`) + +Each schema is generated into its own subfolder: + +- `generated/simpulseid/` +- `generated/harbour/` +- `generated/gx/` + +## 1) Generate + +From repository root: + +```bash +python3 src/generate_from_linkml.py \ + --model linkml/simpulseid.yaml \ + --model linkml/harbour.yaml \ + --model service-characteristics/linkml/gaia-x.yaml \ + --out-root generated +``` + +## 2) Validate examples against generated SHACL + schema (recommended) + +Examples are validated using the `ontology-management-base` validator. +Point `--root` to `generated/` so it can resolve: + +- generated contexts +- generated SHACL files +- generated ontologies + +Example: + +```bash +python3 ontology-management-base/src/check_jsonld_against_shacl_schema.py \ + examples/simpulseid-administrator-credential.json \ + --root generated/ +``` + +## Notes on `id` / `@id` + +In JSON-LD, the node identifier is the keyword `@id`. Many JSON-LD contexts (including VC contexts) map a compact term like `id` to `@id`, but the underlying mechanism is JSON-LD itself. + +For LinkML, the robust modelling pattern is to define a shared slot once (e.g., in a small `core.yaml`) with: + +- `slot_uri: "@id"` for the `id` slot +- `slot_uri: "@type"` for the `type` slot + +and import that shared definition into all schemas that need it. This prevents “conflicting URIs for item: id” during import merges. diff --git a/src/generate_from_linkml.py b/src/generate_from_linkml.py new file mode 100644 index 0000000..26c0a79 --- /dev/null +++ b/src/generate_from_linkml.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +""" +Generate downstream artefacts (JSON-LD context, SHACL shapes, OWL ontology) +from one or more LinkML schemas. + +Examples: + python3 src/generate_from_linkml.py --model linkml/simpulseid.yaml + python3 src/generate_from_linkml.py --model linkml/simpulseid.yaml --model linkml/harbour.yaml +""" + +import argparse +import os +from pathlib import Path +from typing import Dict, List, Optional +from urllib.parse import urlparse + +from linkml.generators.jsonldcontextgen import ContextGenerator +from linkml.generators.owlgen import OwlSchemaGenerator +from linkml.generators.shaclgen import ShaclGenerator as _BaseShaclGenerator +from linkml_runtime.utils.schemaview import SchemaView +import json + +def patch_jsonld_keywords(context_json: str) -> str: + """ + LinkML cannot use '@id'/'@type' as slot_uri (loader rejects it). + We therefore patch the generated context to alias: + - id -> @id + - type -> @type + """ + doc = json.loads(context_json) + ctx = doc.get("@context") + if isinstance(ctx, dict): + ctx["id"] = "@id" + ctx["type"] = "@type" + return json.dumps(doc, indent=2, ensure_ascii=False) + "\n" + + +def debug(msg: str) -> None: + print(f"[DEBUG] {msg}") + + +def ensure_dir(path: Path) -> None: + path.mkdir(parents=True, exist_ok=True) + + +def find_repo_root(start: Path) -> Path: + current = start + while current != current.parent: + if (current / ".git").is_dir() or (current / "service-characteristics").is_dir(): + return current + current = current.parent + return start.parent + + +def build_import_map(repo_root: Path) -> Dict[str, str]: + gaiax_linkml_dir = repo_root / "service-characteristics" / "linkml" + import_map: Dict[str, str] = {} + + if not gaiax_linkml_dir.is_dir(): + debug(f"Gaia-X linkml dir not found at {gaiax_linkml_dir}") + return import_map + + for yaml_file in gaiax_linkml_dir.glob("*.yaml"): + import_map[yaml_file.stem] = str(yaml_file.with_suffix("").resolve()) + + debug(f"Built import_map with {len(import_map)} entries from {gaiax_linkml_dir}") + return import_map + + +def iri_to_model_name(iri: str) -> str: + path = urlparse(iri).path + parts = [p for p in path.split("/") if p] + return (parts[0].lower() if parts else "unknown") + + +class FixedShaclGenerator(_BaseShaclGenerator): + """Ensure SchemaView is built with the same importmap/base_dir as the loader.""" + + def __post_init__(self) -> None: + from linkml.utils.generator import Generator as BaseGenerator + + BaseGenerator.__post_init__(self) + self.schemaview = SchemaView(self.schema, importmap=self.importmap or {}, base_dir=self.base_dir) + self.generate_header() + + +def set_linkml_model_path(repo_root: Path) -> None: + gaiax_linkml_dir = repo_root / "service-characteristics" / "linkml" + local_linkml_dir = repo_root / "linkml" + + search_paths: List[str] = [] + if gaiax_linkml_dir.is_dir(): + search_paths.append(str(gaiax_linkml_dir)) + if local_linkml_dir.is_dir(): + search_paths.append(str(local_linkml_dir)) + + existing_env = os.environ.get("LINKML_MODEL_PATH") + if existing_env: + search_paths.append(existing_env) + + if search_paths: + os.environ["LINKML_MODEL_PATH"] = os.pathsep.join(search_paths) + debug(f"LINKML_MODEL_PATH set to: {os.environ['LINKML_MODEL_PATH']}") + + +def schema_id_for(model_path: Path, import_map: Dict[str, str], base_dir: str) -> Optional[str]: + sv = SchemaView(str(model_path), importmap=import_map, base_dir=base_dir) + return getattr(sv.schema, "id", None) + + +def generate_one(model_path: Path, out_root: Path, import_map: Dict[str, str]) -> None: + base_dir = str(model_path.parent) + + sid = schema_id_for(model_path, import_map, base_dir) + model_name = iri_to_model_name(sid) if sid else model_path.stem.lower() + + out_dir = out_root / model_name + ensure_dir(out_dir) + + out_context = out_dir / f"{model_name}_context.jsonld" + out_shacl = out_dir / f"{model_name}_shacl.ttl" + out_owl = out_dir / f"{model_name}_ontology.ttl" + + debug(f"Model: {model_path}") + debug(f"schema_id={sid!r} -> model_name={model_name!r}") + debug(f"Outputs: {out_dir}") + + old_cwd = Path.cwd() + os.chdir(model_path.parent) + try: + print(f"Using LinkML model: {model_path}") + + print(f"Generating JSON-LD context -> {out_context}") + ctx_gen = ContextGenerator(str(model_path), importmap=import_map, base_dir=base_dir) + out_context.write_text(patch_jsonld_keywords(ctx_gen.serialize()), encoding="utf-8") + + print(f"Generating SHACL shapes -> {out_shacl}") + shacl_gen = FixedShaclGenerator(str(model_path), importmap=import_map, base_dir=base_dir) + out_shacl.write_text(shacl_gen.serialize(), encoding="utf-8") + + print(f"Generating OWL ontology -> {out_owl}") + owl_gen = OwlSchemaGenerator(str(model_path), importmap=import_map, base_dir=base_dir) + out_owl.write_text(owl_gen.serialize(), encoding="utf-8") + + print(f"Done: {model_name}") + finally: + os.chdir(old_cwd) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate JSON-LD context, SHACL shapes and OWL ontology from one or more LinkML models." + ) + parser.add_argument( + "--model", + action="append", + required=True, + help="Path to a LinkML YAML schema. Repeat --model for multiple schemas.", + ) + parser.add_argument( + "--out-root", + default="generated", + help="Root output folder (default: generated). Each schema is generated into generated//", + ) + + args = parser.parse_args() + + models = [Path(m).resolve() for m in args.model] + for mp in models: + if not mp.exists(): + raise SystemExit(f"LinkML model not found: {mp}") + + repo_root = find_repo_root(models[0].parent) + debug(f"repo_root: {repo_root} (exists={repo_root.exists()}, type={'dir' if repo_root.is_dir() else 'missing'})") + + import_map = build_import_map(repo_root) + set_linkml_model_path(repo_root) + + out_root = Path(args.out_root).resolve() + ensure_dir(out_root) + + for mp in models: + generate_one(mp, out_root, import_map) + + +if __name__ == "__main__": + main()