youid
Skill42 starsChanged 50 days agoMITView on GitHub
Generate, verify, and manage Web-scale verifiable digital identities (NetIDs) using semantic web standards. Produces X.509 certificates (self-signed or CA-signed via Let's Encrypt/ZeroSSL), WebID profile documents (Turtle, JSON-LD, RDFa HTML), identity card HTML pages with optional OPAL AI widget, vCard VCF, and complete linked-data identity bundles. Uploads identity documents to WebDAV/LDP endpoints via curl. Verifies existing WebID profiles by fetching and executing SPARQL queries against URIBurner. Triggers on phrases like "generate a NetID", "create a YouID identity card", "generate a WebID profile for", "create a WebID profile", "verify WebID", "verify identity at", "generate X.509 certificate for", "upload identity documents to", "delegate identity for", "what is a NetID", "define WebID", "explain YouID", "self-sovereign identity", "digital identity card", "WebID-TLS", "DPKI certificate", "generate a did:nostr identity", "create an agent identity", "nostr keypair for", "link my nostr key to my WebID", "resolve did:nostr", "verify nostr link". Also mints did:nostr (Nostr / secp256k1) identities via create-agent, links them to a WebID (same agent via owl:sameAs, or an agent acting on behalf of the WebID via oplcert:hasIdentityDelegate), and resolves/verifies the link in both directions. Compatible with the OpenLink YouID browser extension.
In this repository45 other files it gives its agents.
- agent-rdf-memory/AGENTS.mdAGENTS.md
- AGENTS.mdAGENTS.md
- a2a-clientSkill
- acp-clientSkill
- csv-to-rdf-det-generatorSkill
- data-twinglerSkill
- dbpedia-query-skillSkill
- document-to-kg-skillSkill
- fediverse-crudSkill
- fuxi-engineerSkill
- infographic-describerSkill
- iodbc-dsn-managerSkill
- kg-generatorSkill
- linked-data-skillsSkill
- llm-routing-skillSkill
- monidSkill
- mpp-stripe-clientSkill
- mtls-curlSkill
- opal-agent-skill-assemblerSkill
- openlink-license-managerSkill
- openlink-license-readerSkill
- openlink-request-broker-configuratorSkill
- opml-rss-readerSkill
- osdi-inclusion-engineSkill
- pinchtabSkill
- rdf-det-variant-generatorSkill
- rdf-infographic-skillSkill
- resource-access-offers-generatorSkill
- rss-feed-generatorSkill
- s3-query-skillSkill
- screencast-recorderSkill
- set-webdav-resource-propertySkill
- sparql-faceted-browserSkill
- ucp-clientSkill
- uriburner-opal-agent-skillsSkill
- virtuoso-rdf-bulk-loaderSkill
- virtuoso-rdf-loaderSkill
- virtuoso-support-agentSkill
- wc2026-match-reportSkill
- weblog-from-webdavSkill
- website-from-webdavSkill
- wikidata-query-skillSkill
- world-cup-2026-navigatorSkill
- x402-buyerSkill
- xmla-clientSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
39 rules. About 0 are common, 23 are distinctive, and 16 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈23
Distinctive · estimate
This author's own choices. Worth a look.
16
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 73
Use this skill whenever the user wants to:
- Distinctive: line 118
When the user explicitly invokes the YouID skill by name or a clear identity-generation trigger, the skill operates in Strict Harness Mode.
- Distinctive: line 122
Every generated artifact must have valid RDF or HTML syntax.
- Very specific: line 123
Every <link> and <a href> in HTML output must resolve to IRIs that match the corresponding RDF entity subjects in the profile documents.
- Distinctive: line 138
No external CSS or JS frameworks (Bootstrap, jQuery) — pure inline CSS
- Distinctive: line 144
Before delivering any output to the user, the following MUST pass:
- Distinctive: line 148
All artifact files exist in the output directory
- Distinctive: line 183
Default storage URL (optional, PIM)
- Distinctive: line 189
Ask: *"What type of X.509 certificate?
- Very specific: line 190
If letsencrypt or zerossl selected, collect ACME-specific parameters:
- Distinctive: line 193
Use Let's Encrypt staging?
- Very specific: line 194
If zerossl, ask: *"What is your ZeroSSL API key?
- Distinctive: line 196
Elicit template style: after collecting parameters:
- Distinctive: line 197
Ask: *"Which profile page style would you like?
- Distinctive: line 198
Ask: *"What URL should we use for your profile photo?"* (or skip to use the default placeholder)
- Distinctive: line 199
If Dark style selected, collect a bio summary: *"Write a short bio or summary about the subject for the profile page."*
- Distinctive: line 202
If certificate validity was not provided, ask: *"How many years should the X.509 certificate be valid?
- Very specific: line 204
Generate certificate: Run scripts/generate_identity.sh with the collected parameters (or scripts/generate_certificate.sh directly).
- Very specific: line 211
Fill templates: Run scripts/generate_identity.sh with:
- Distinctive: line 234
Check entity IRIs match between files
- Distinctive: line 264
Ask: *"Self-signed, or CA-signed via Let's Encrypt/ZeroSSL?"*
- Very specific: line 265
If ACME: collect --acme-domain (parsed from WebID if omitted), --acme-email, and for ZeroSSL --zerossl-api-key
- Very specific: line 266
Run scripts/generate_certificate.sh with positional args (self-signed) or named args (--mode, --common-name, etc.) to produce .pem, .crt, …
- Very specific: line 274
Run scripts/verify_webid.sh or use URIBurner SPARQL endpoint
- Very specific: line 291
Use curl PUT/PROPPATCH commands from references/upload-backends.md
- Distinctive: line 293
Verify uploads with HEAD requests
- Very specific: line 321
If provided, oplcert:onBehalfOf will be added to the delegate's local files too
- Distinctive: line 322
If not provided, only the delegator's files are modified
- Very specific: line 437
WebID side of the link: run T1/T2 with -N <did> -R <same mode>.
- Very specific: line 450
Secret handling: never print the secret, never put it in the output bundle, and never commit it.
- Very specific: line 476
All template files in templates/ use the YouID template syntax:
- Distinctive: line 502
Before running any generation workflow, you MUST:
- Very specific: line 520
If acme.sh is not installed, the script will auto-install it via curl.
- Distinctive: line 521
If ZeroSSL mode, verify the user has a ZeroSSL API key ready.
- Distinctive: line 530
Before delivering any generated identity to the user:
- Distinctive: line 585
WebID SAN is mandatory: Every generated X.509 certificate MUST have the user's WebID URI as a Subject Alternative Name.
- Very specific: line 588
Multiple output formats are not optional: Always generate all 6 RDF formats (Turtle, JSON-LD, RDFa HTML for profile, certificate, and …
- Very specific: line 592
When generating index.html, every social/profile platform URL in owl:sameAs / schema:sameAs needs a branded 32×32 icon in .social-grid.
- Distinctive: line 628
When adding social platform URLs to a credential set, ALL four profile files must be updated consistently:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
YouID Skill — Web-Scale Verifiable Digital Identity
Version: 1.2.0
Operating Modality — Read This First
You are a modern UI/UX expert specialising in digital identity credential design for the duration of any task that uses this skill. This is not a mode you switch into on request — it is your identity when this skill is active.
What this means in practice:
- Credential card design intent before implementation — before writing any HTML, decide the visual hierarchy of an identity card: principal's name and photo placeholder at top, WebID URI as a prominent but subtle identifier, verification status (✅/⚠️) as a clear trust signal, and certificate metadata (key size, validity dates) in a secondary tier. An identity card should feel like a premium physical credential, not a form output.
- Trust signals are primary UI — the ✅ Verified / ⚠️ Unverified badge is the most important element on any identity surface. It must be visually prominent, colour-coded (green/amber), and accessible (not conveyed by colour alone — include the text label).
- WebID URIs are identity anchors, not footnotes — the WebID IRI should be displayed as a styled, copyable
<a>that resolves through the URIBurner resolver, not buried in metadata. It is the primary dereferenceable identifier for the person. - Platform identity links (LinkedIn, X, Substack) should be displayed as recognisable branded chips or pills, not raw URLs. Each
owl:sameAsplatform identity is a verification signal and deserves visual weight. - Colour token discipline — use CSS variables.
--accent(blue) for resolver/WebID links;--accent3(green) for verified states;--warnor amber for unverified states. Never hardcode hex values for trust-signal colours. - RDFa HTML is both data and display — the
profile_rdfa.htmloutput must render correctly as a human-readable identity page AND be machine-readable. Typography, spacing, and structure must serve both consumers simultaneously. - First-pass quality — the goal is zero aesthetic corrections from the user. Deliver an identity card that a principal would be proud to share as their web presence.
2026-09-28 Updates (v1.2.0) — did:nostr support:
- New T8 (
scripts/generate_nostr_identity.sh): mints a did:nostr identity via create-agent, writes the DID side of a WebID link, and supports optional relay services, macOS Keychain custody, an encrypted secp256k1.p12backup, and a.well-knownHTTP-resolution copy generate_identity.sh -N <did:nostr|npub|hex> -R same|agentadds the WebID side of the link, plus asec:Multikey, toprofile.ttl,profile.jsonld, and the RDFa inprofile_rdfa.html/index.html. It also adds a Nostr Identity card section (all 3 styles) and a vCardnostr:URI- New Step 7 gate (
scripts/nostr_link_gate.py), which checks the link and Multikey across all 4 representations. The Step 6 delegation gate exemptsdid:nostr:delegates from the RSAcert:keyrule - New
scripts/resolve_did.py: did:nostr resolution (HTTP.well-known/ offline minimal / relay-enhanced, with every relay event signature-checked), two-sidedlinkverification, andproveproof-of-control from the git, Keychain, or.p12key - New
scripts/nostr_crypto.py(stdlib secp256k1 + BIP-340 + bech32;selftestruns the BIP-340 vectors) andscripts/nostr_relay.mjs(dependency-free NIP-01 client) - Fixed a pre-existing bug: Step 5 ran
unset OUT_DIR, which made the Step 6 delegation gate silently scan nothing and the final summary exit 1 underset -u - Reference:
references/did-nostr.md
2026-07-08 Updates (v1.1.0):
- Added ACME certificate support: Let's Encrypt and ZeroSSL cert type modalities
- Created
scripts/acme_helper.sh: ACME.sh orchestration (install, CSR generation with DNS+URI SANs, signing, download) - Extended
generate_certificate.shwith--mode self-signed|letsencrypt|zerosslflag - Extended
generate_identity.shwith-M <cert_type>,-D <domain>,-Z <zerossl_key>flags - Created
references/acme-workflows.md: ACME reference documentation - Added
ca_cert_urlandcert_typefields tocert_data.jsonoutput (enables!!{ca_cert_url}template conditionals) - Updated T1/T2/T3 workflows with cert type elicitation steps
- ACME mode auto-installs
acme.shif missing; fetches ZeroSSL EAB credentials via API
2026-06-18 Updates:
- Fixed exponent extraction bug in
generate_certificate.sh:107—grep -A1capturedX509v3extensions:instead of65537. Replaced withawk '/Exponent:/ {print $2}'. - Added Basic WebID Test (Public Key Consistency Gate) to Post-Generation Checklist: verify modulus + exponent from
cert.p12matchindex.html,profile.ttl, andprofile.jsonld. - Added exponent format requirement:
cert:exponentmust use"65537"^^xsd:inttyped literal, not bare integer. - Fixed
profile_rdfa.html.tpl:%{pdp_url_head}→!{pdp_url_head}, added!!{rel_header_html}conditional block.
When to Use
Use this skill whenever the user wants to:
- Generate a WebID profile (Turtle, JSON-LD, RDFa HTML, vCard VCF)
- Create a full NetID identity bundle (profile + cert + public key + identity card HTML)
- Generate an X.509 certificate bound to a WebID URI (SAN) — self-signed or CA-signed via Let's Encrypt/ZeroSSL via ACME
- Verify an existing WebID profile by fetching and SPARQL-querying it
- Upload identity documents to WebDAV/LDP storage backends
- Explain semantic web identity concepts (WebID, FOAF, cert ontology, DPKI, NetID)
Trigger Phrases
| Trigger | Action |
| "Generate a WebID profile for {user}" | Collect identity data, generate profile in all formats |
| "Create a NetID / YouID identity card" | Full identity bundle: profile + cert + card HTML + vCard |
| "Generate an X.509 certificate for {WebID}" | Create .pem/.p12 with WebID SAN (self-signed or ACME) |
| "Generate a Let's Encrypt / ZeroSSL certificate for {WebID}" | Create CA-signed .pem/.p12 via ACME |
| "Sign my certificate with Let's Encrypt / ZeroSSL" | Sign existing CSR with an ACME CA |
| "Verify WebID {url}" / "Check identity at {url}" | Fetch and SPARQL-verify the WebID profile |
| "Upload identity documents to {destination}" | Upload generated artifacts to WebDAV/LDP |
| "Delegate identity for {WebID}" | Generate On-Behalf-Of delegation profile |
| "What is {X}?" (WebID, NetID, DPKI, FOAF, etc.) | Explain semantic web identity concepts |
| "Generate a did:nostr / agent identity", "nostr keypair for {agent}" | T8 — mint via create-agent, optionally link to a WebID |
| "Link my nostr key / agent to my WebID" | T8 + T1/T2 with -N … -R same|agent (both sides of the link) |
| "Resolve did:nostr:…", "verify the nostr link for {WebID}" | T8 verification via scripts/resolve_did.py |
Defaults & Settings
| Setting | Default | Description |
| RSA key size | 2048 | RSA key pair size in bits |
| Certificate validity | 365 days (1 year) | Self-signed cert validity period; elicited from user via T1/T3 |
| Digest algorithm | sha256 | Hash for certificate fingerprint |
| Template engine | scripts/template_fill.py | Python script for %{key}/!{key}/!!{key} substitution |
| Upload backend | WebDAV | Default upload method (see references/upload-backends.md) |
| Output directory | ./youid-output/ | Generated files land here |
| Bundle format | Plain directory | Optionally ZIP with zip -r |
| Certificate type | self-signed | Certificate signing: self-signed, letsencrypt, or zerossl. Elicited from user via T1/T2/T3 with ACME-specific parameters |
| ACME domain | Parsed from WebID | Domain used for ACME domain validation (extracted from WebID URI hostname) |
| ACME staging | false | Use Let's Encrypt staging endpoint for testing (--acme-staging) |
| ACME email | From --acme-email or email param | Contact email for ACME account registration and renewal |
| ZeroSSL EAB | Required for zerossl mode | Fetch via ZeroSSL API: POST /acme/eab-credentials?access_key=<key> |
Harness Mode
When the user explicitly invokes the YouID skill by name or a clear identity-generation trigger, the skill operates in Strict Harness Mode. In this mode all gates below are non-negotiable.
Delivery Contract
- Every generated artifact must have valid RDF or HTML syntax.
- Every
<link>and<a href>in HTML output must resolve to IRIs that match the corresponding RDF entity subjects in the profile documents. - The X.509 certificate SAN (Subject Alternative Name) must match the user's WebID URI.
- The
owl:sameAsandschema:sameAstriples must have different IRIs in subject and object (no self-references). - Generated IRIs must use the resolver pattern:
https://linkeddata.uriburner.com/describe/?url={url-encoded-iri}for entity hyperlinks. - All
schema:properties and types must be real schema.org terms — no fabricated IRIs. - Identity card template contract (applies to all 3 templates:
index.html.tpl,index_premium.html.tpl,index_dark.html.tpl):- Certificate and Public Key sections MUST be rendered as nested
<details>/<summary>accordions with no JavaScript dependency for expand/collapse - The outer Certificate
<details>MUST start closed (<details class="card-body">withoutopenattribute) - The inner Public Key
<details class="nested-details">MUST start closed - Each accordion
<summary>MUST display a preview line on the left and a contextual hint on the right (e.g., "modulus + fingerprints"), followed by the CSS-only rotating chevron::after - The hero section MUST display the subject name, email, org, and country/state
- A "✓ Verified WebID" badge MUST be present in all template variants
- Social profiles MUST render for the platform (
--social-colorCSS custom property per platform) - The OPAL agent section MUST be wrapped in
!!{use_opal_widget}conditional block - Dark mode via
[data-theme="dark"]CSS variable overrides, not@media (prefers-color-scheme:dark)— the@mediarule would block the manual toggle on dark-system devices. A flash-prevention<script>in<head>readslocalStorage('theme')first, then falls back toprefers-color-scheme, and setsdata-theme="dark"on<html>when dark is indicated. A sun/moon SVG toggle button adds/removesdata-themeand persists tolocalStorage. Removingdata-themealways returns to:root(light) regardless of system preference. - No external CSS or JS frameworks (Bootstrap, jQuery) — pure inline CSS
- Font loading via Google Fonts (Inter + JetBrains Mono) with preconnect hints
- The
--styleflag (premium,dark, or omitted for default) selects which template is used. The output file is alwaysindex.htmlregardless of selected template.
- Certificate and Public Key sections MUST be rendered as nested
GATE: Zero Failures Required
Before delivering any output to the user, the following MUST pass:
- Generated Turtle/JSON-LD/RDFa files parse correctly
- X.509 certificate has proper WebID SAN
owl:sameAshas no self-references- All artifact files exist in the output directory
- Basic WebID Test (Public Key Consistency Gate): RSA public key (modulus + exponent) from
cert.p12matchesindex.html,profile.ttl, andprofile.jsonld— automated in Step 5 ofgenerate_identity.sh, blocks generation on failure - Delegation & Cert:Key Consistency Gate: If any output file contains
oplcert:hasIdentityDelegateoroplcert:onBehalfOftriples, they MUST match identically acrossprofile.ttl,profile.jsonld,profile_rdfa.html, andindex.html(all 4 representations: POSH, embedded JSON-LD, embedded Turtle, hidden RDFa) — automated in Step 6 ofgenerate_identity.sh, blocks generation on failure. As of 2026-09-13, the gate also requires every delegate named inhasIdentityDelegateto have its owncert:key(RSA modulus+exponent) republished under the delegator's documents, consistently across all four representations, with matching modulus values wherever inlined. This closes a real gap found live: the relation triples alone were correctly generated and consistent, but the delegate's actual public key — which is what a standard WebID-TLS verifier checks, not the relation vocabulary — was present in only one of the four files, silently defeating delegation for any resource-access check that relies on the standard key-lookup mechanism. Seeagent-rdf-memory/howto/webid-tls-on-behalf-of-delegation-no-effect-incident-report...andpreferences.ttlfor the live-verified finding this gate now prevents from recurring.
Execution Routing
| Priority | Method | When |
| 1 | scripts/generate_identity.sh | Local cert generation + template fill (deterministic, no LLM drift) |
| 2 | scripts/verify_webid.sh + curl | WebID verification via SPARQL against URIBurner |
| 3 | URIBurner REST (Demo.demo.execute_spasql_query) | WebID verification via SPASQL endpoint |
| 4 | curl (WebDAV/LDP PUT) | Upload identity documents to storage backends |
| 5 | LLM-guided | Collect identity parameters, explain ontologies, answer concept questions |
When to Use Each
- scripts/generate_identity.sh — Use for ALL identity generation (cert + profiles + card). This is the primary execution path. Use
-M letsencryptor-M zerosslfor ACME-signed certificates. The script handles deterministic cert generation, ACME.sh orchestration, and template filling. Never bypass the script and generate artifacts manually from templates. - scripts/verify_webid.sh — Use when the user asks to verify a WebID profile. Fall back to URIBurner SPASQL (Demo.demo.execute_spasql_query) if the script fails.
- curl — Use for WebDAV/LDP upload. Follow the exact curl commands in
references/upload-backends.md. - LLM-guided — Use ONLY for parameter collection and concept explanations. Never for generating final artifacts.
Order of Operations
T1 — Generate WebID Profile
- Collect parameters from the user:
- WebID URI (required — e.g.,
https://example.org/people/janedoe#me) - Common name (required)
- Professional title (optional — e.g., "Founder & CEO, OpenLink Software")
- Email (optional)
- Organization (optional)
- Country code (optional, 2-letter ISO)
- State/Province (optional)
- Photo URL (optional, elicited separately — ask for a URL)
- Personal profile page URL (optional, PDP)
- Default storage URL (optional, PIM)
- WebID URI (required — e.g.,
- Social relation links (optional, list of platform URLs)
- Bio summary (optional, free-text, for dark template bio section; set as
subj_summaryin extra data JSON) - Certificate validity in years (optional, default 1)
- Nostr identity to link (optional): a
did:nostr:…,npub1…, or 64-hex key (mint one first with T8 if needed). If given, you MUST ask "Whose identity is this?": "(1) Mine — the same agent as this WebID (owl:sameAs), or (2) a separate agent acting on my behalf (oplcert:hasIdentityDelegate)?" Pass it as-N <id> -R same|agent. Never assumesame; seereferences/did-nostr.md - Certificate type (optional, default
self-signed):- Ask: "What type of X.509 certificate? (1) Self-signed — quick, no external dependency. (2) Let's Encrypt — publicly trusted CA, needs domain ownership validation. (3) ZeroSSL — publicly trusted CA, needs API key for External Account Binding."
- If
letsencryptorzerosslselected, collect ACME-specific parameters:- ACME domain (default: parsed from WebID URI hostname)
- Contact email for ACME account registration (default: subject email or
contact@{domain}) - Use Let's Encrypt staging? (default: no — production) — recommend staging for testing
- If
zerossl, ask: "What is your ZeroSSL API key? (create one at https://app.zerossl.com/developers)"
- Elicit template style after collecting parameters:
- Ask: "Which profile page style would you like? (1) Default — gradient hero, compact layout. (2) Premium — clean white hero, larger photo, credential badge bar. (3) Dark — dark hero, circular photo, question chips, dual CTA."
- Ask: "What URL should we use for your profile photo?" (or skip to use the default placeholder)
- If Dark style selected, collect a bio summary: "Write a short bio or summary about the subject for the profile page."
- Set
profile_stylein extra data JSON:"premium","dark", or omit for default - Set
photo_urland optionallysubj_summaryin extra data JSON if provided - If certificate validity was not provided, ask: "How many years should the X.509 certificate be valid? (default: 1)"
- Generate certificate: Run
scripts/generate_identity.shwith the collected parameters (orscripts/generate_certificate.shdirectly). For ZeroSSL mode, the script auto-installsacme.shif missing and fetches EAB credentials. Pass validity as the 9th positional argument or--validity-daysfor named-arg mode. This creates:cert.pem— PEM-encoded X.509 certificate (fullchain for ACME modes)cert.crt— DER-encoded X.509 certificatecert.p12— PKCS#12 bundle (cert + key, password-protected)cert_data.json— extracted fields (modulus, exponent, fingerprints, NI/DI URIs, dates, serial, issuer, ca_cert_url, cert_type)ca.cer— CA certificate (ACME modes only)
Fill templates: Run
scripts/generate_identity.shwith:- The certificate data file
- User's identity parameters
- A base URL for artifact IRIs (e.g., the WebDAV directory where files will be hosted)
- The
--styleflag orprofile_stylein extra data JSON to select identity card template
This produces:
profile.ttl— Turtle profile document (FOAF + schema.org + cert + oplcert)profile.jsonld— JSON-LD profile documentprofile_rdfa.html— RDFa HTML profile pagecertificate.ttl— Certificate metadata in Turtlecertificate.jsonld— Certificate metadata in JSON-LDcertificate.rdfa.html— Certificate metadata in RDFa HTMLpublic_key.ttl— Public key information in Turtlepublic_key.jsonld— Public key information in JSON-LDpublic_key.rdfa.html— Public key information in RDFa HTMLvcard.vcf— vCard VCF contact cardstyle.css— Card stylingindex.html— Identity card HTML page (using selected template, with conditional OPAL widget)
- Verify output: Validate each generated artifact.
- Parse
.ttlfiles with rdflib - Validate X.509 cert with openssl
- Check entity IRIs match between files
- Parse
- Deliver to the user: present the output directory path and ask if they want to upload.
T2 — Create Full NetID Identity Card
Same as T1 (including certificate type elicitation: self-signed, Let's Encrypt, or ZeroSSL) but with OPAL widget configuration collected as additional parameters:
- Enable OPAL widget (yes/no)
- OPAL endpoint URL (e.g.,
https://linkeddata.uriburner.com) - Auth mode (
beareroroauth) - API key (if bearer auth)
- Widget mode (
opalbasic oropalxenhanced with file uploads/assistants) - Assistant ID (if opalx mode)
- Agent config name — the fine-tune module/agent config loaded server-side (e.g.,
virtuoso-support-assistant-config,new-sparql-agent-116,data-twingler-config,chat-help). Default:virtuoso-support-assistant-config. The.jsonsuffix is omitted — only the config name is supplied. Elicit from user with: "Which OPAL agent config should the chat use? (default: virtuoso-support-assistant-config)" - Model, temperature, top_p settings
- Predefined prompts (up to 4)
The index.html.tpl template includes conditional !!{use_opal_widget} / !!{use_opalx} / !!{use_opal} blocks that the template engine fills when these settings are provided.
T3 — Generate X.509 Certificate
- Collect parameters from the user:
- WebID URI (required)
- Common name (required)
- Email (optional)
- Organization (optional)
- Country (optional)
- Password (default: youid)
- Certificate validity (years, default 1)
- Certificate type (default: self-signed):
- Ask: "Self-signed, or CA-signed via Let's Encrypt/ZeroSSL?"
- If ACME: collect
--acme-domain(parsed from WebID if omitted),--acme-email, and for ZeroSSL--zerossl-api-key
- Run
scripts/generate_certificate.shwith positional args (self-signed) or named args (--mode,--common-name, etc.) to produce .pem, .crt, .p12, cert_data.json - Display fingerprint, modulus, exponent, issuer, validity dates, and CA cert URL (ACME) to the user
- Deliver the certificate files
T4 — Verify WebID Profile
- Fetch the WebID URL using curl
- Determine the RDF serialization format (content-type negotiation or heuristic)
- Run
scripts/verify_webid.shor use URIBurner SPARQL endpoint - Report: name(s), public keys (modulus, exponent, fingerprint), certificates (including CA signer for ACME certs), delegates, storage locations, inbox, outbox, foaf:knows relations, email
- For ACME-signed certificates, additionally verify:
- Issuer DN references the CA, not the subject
oplcert:IANtriple present (CA certificate URL)- Certificate chain validates:
openssl verify -CAfile ca.cer cert.pem - DNS Subject Alternative Name matches the ACME domain
Verification SPARQL queries are in references/verification-queries.md.
T5 — Upload Identity Documents
- Determine the upload backend from user's destination URL:
- OpenLink WebDAV (DAV home)
- OpenLink LDP
- Generic LDP over TLS
- WebDAV over HTTPS
- Use curl PUT/PROPPATCH commands from
references/upload-backends.md - Upload all files from the output directory (5–15 files depending on config; ACME modes also upload
ca.cer) - Verify uploads with HEAD requests
T6 — Generate On-Behalf-Of Delegation
Workflow-start elicitation (MUST collect before any generation):
- Elicit the Delegator's WebID/NetID — the identity granting authority:
- "What is the WebID or NetID of the entity granting delegation authority?"
- May be a URL from the user's own identity or a previously generated YouID card URL
- Elicit the Delegate's WebID/NetID — the entity that will act on-behalf-of:
- "What is the WebID or NetID of the entity that will act on behalf of the delegator?"
- May be a person, organization, or software agent
- Elicit the Delegation Role — determines the scope of delegated authority:
- "What delegation role should be granted? Choose one:
- identify — Delegate may assert the delegator's identity
- inform — Delegate may receive information intended for the delegator
- consult — Delegate may be consulted on behalf of the delegator
- authority — Delegate has full authority to act as the delegator"
- "What delegation role should be granted? Choose one:
- Elicit the Delegator's local directory — MUST ask at workflow start:
- "What is the local directory path for the delegator's YouID profile files?"
- This is where the profile documents (
profile.ttl,profile.jsonld,profile_rdfa.html,index.html) reside - The directory is usually inside the user's local credentials folder
- Elicit the Delegate's local directory — ask if the delegate also has local profile files:
- "Does the delegate have a local directory with profile files? If so, what is the path?"
- If provided,
oplcert:onBehalfOfwill be added to the delegate's local files too - If not provided, only the delegator's files are modified
Apply delegation to local files:
Key rule: In local file deployment, each identity's documents only declare their side of the relation.
- Delegator's documents —
oplcert:hasIdentityDelegateonly (grants authority)- Delegate's documents —
oplcert:onBehalfOfonly (acknowledges authority) This differs from SPARQL UPDATE deployment where both triples may reside in a single graph (seereferences/delegation.md).
Add
oplcert:hasIdentityDelegateto the delegator's local profile files — every RDF representation MUST contain the triple:File Representation What to add profile.ttlTurtle oplcert:hasIdentityDelegate <delegate-webid> .on the#netidentityprofile.jsonldJSON-LD "oplcert:hasIdentityDelegate": { "@id": "<delegate-webid>" }in the#netidgraph entryprofile_rdfa.htmlRDFa + JSON-LD <div rel="oplcert:hasIdentityDelegate" resource="<delegate-webid>"></div>AND add to embedded JSON-LD graphindex.htmlPOSH (hero <header>)<link property="oplcert:hasIdentityDelegate" href="<delegate-webid>" />after the existing<link property="cert:key">index.htmlEmbedded JSON-LD Graph entry with "oplcert:hasIdentityDelegate": { "@id": "<delegate-webid>" }— also add@type: "foaf:Agent"andschema:additionalType: "Delegator"index.htmlEmbedded Turtle oplcert:hasIdentityDelegate <delegate-webid> .on the#netidentityindex.htmlHidden RDFa <div typeof="foaf:Agent" about="<delegator-webid>"><div property="schema:additionalType" content="Delegator"></div><div rel="oplcert:hasIdentityDelegate" resource="<delegate-webid>"></div></div>
6b. REQUIRED, not optional — republish the delegate's cert:key under the delegator's same local profile files. Fixed 2026-09-13: the hasIdentityDelegate/onBehalfOf triples above only document the relationship's intent — confirmed live that no tested resource-server authorization path (WAC/ACL, MPP payment-entitlement, or a remote WebID-TLS verification service) independently checks them. A standard WebID-TLS verifier checks cert:key, so skipping this step means the delegation silently does nothing for actual access. Extract the delegate's RSA modulus + exponent from its own cert.pem/cert.p12 (or dereference its live WebID profile if no local file is available — see scripts/generate_delegation.sh -k), then add to every file alongside the table in step 6:
| File | Representation | What to add |
profile.ttl | Turtle | <delegate-webid> cert:key <key-node> . plus a cert:RSAPublicKey node with cert:modulus/cert:exponent (inline, or an indirection to a public_key.ttl-style file — match whichever pattern the delegator's OWN key already uses in this file) |
profile.jsonld | JSON-LD | Same shape as profile.ttl, in @graph |
profile_rdfa.html | RDFa | <div about="<delegate-webid>"><div rel="cert:key" resource="#X"></div></div> plus <div typeof="cert:RSAPublicKey" about="#X"> with cert:modulus/cert:exponent properties |
index.html | Embedded JSON-LD AND embedded Turtle (both — index.html carries both blocks) | Same shape as above, local fragment (e.g. #DelegatePublicKey) is fine since it only needs to resolve within this document |
generate_identity.sh's Step 6 gate now enforces this automatically — it fails generation if any delegate lacks a cert:key in any of the four files, or if the modulus disagrees across files that inline it. Do not treat a gate pass on hasIdentityDelegate/onBehalfOf consistency alone as sufficient; the gate checks both, but if you're applying this by hand outside the generator, verify both yourself.
If delegate directory provided, add
oplcert:onBehalfOfto the delegate's local profile files using the same index.html coverage:File Representation What to add profile.ttlTurtle oplcert:onBehalfOf <delegator-webid> .on the#netidentityprofile.jsonldJSON-LD "oplcert:onBehalfOf": { "@id": "<delegator-webid>" }in the#netidgraph entryprofile_rdfa.htmlRDFa + JSON-LD <div rel="oplcert:onBehalfOf" resource="<delegator-webid>"></div>AND add to embedded JSON-LD graphindex.htmlPOSH (hero <header>)<link property="oplcert:onBehalfOf" href="<delegator-webid>" />after the existing<link property="cert:key">index.htmlEmbedded JSON-LD "oplcert:onBehalfOf": { "@id": "<delegator-webid>" }on the#netidentry — add in both thefoaf:Agentandschema:Persongraph entries if both existindex.htmlEmbedded Turtle oplcert:onBehalfOf <delegator-webid> .on the#netidentityindex.htmlHidden RDFa <div typeof="foaf:Agent" about="<delegate-webid>"><div property="schema:additionalType" content="Delegate"></div><div rel="oplcert:onBehalfOf" resource="<delegator-webid>"></div></div>- Generate
declarativeNetRequestrule — producedelegation-rule.jsonfor the delegate's browser (injectsOn-Behalf-Ofheader):{ "id": 1, "priority": 1, "condition": { "urlFilter": "*", "resourceTypes": ["xmlhttprequest"] }, "action": { "type": "modifyHeaders", "requestHeaders": [ { "header": "On-Behalf-Of", "operation": "set", "value": "<delegate-webid>" } ] } }IMPORTANT: The
On-Behalf-OfHTTP header value must be a bare WebID URI — no angle brackets. When using curl directly:- Correct:
-H "On-Behalf-Of: https://example.com/dataspace/person/user@example.com" - Incorrect:
-H "On-Behalf-Of: <https://example.com/dataspace/person/user@example.com>"Angle brackets cause delegation resolution failure (402/401). The<delegate-webid>placeholder above uses angle brackets as placeholder delimiters — the generated JSON rule will contain the actual bare WebID URI.
- Correct:
- Present changes to the user — show each file that was modified and the exact triple added
- "The delegation triple has been added to the following files. Upload them to the server to deploy: [file list]"
Verify after upload — the remote OBO test is the SAME corroboration the local whoami identity-verification flow does (
agent-rdf-memory/howto/verified-identity.ttlSteps 6-8), just resolved remotely by dereferencing the live WebIDs over HTTP instead of grepping local profile files. This is the PRIMARY check; a third-party verification-service call (below) is a weaker, optional supplement — not a substitute.Primary — dereference both profiles, check the reciprocal triples:
# Delegator side: does the delegator's own profile grant the delegate? curl -sS -L "{delegator-webid-profile-url}" | grep -i "hasIdentityDelegate" # expect: oplcert:hasIdentityDelegate <delegate-webid> . # Delegate side: does the delegate's own profile acknowledge the delegator? curl -sS -L "{delegate-webid-profile-url}" | grep -i "onBehalfOf" # expect: oplcert:onBehalfOf <delegator-webid> (possibly among a list of several) . # Cross-serialization: same triples should also appear in index.html on both sides # (embedded JSON-LD, embedded Turtle, hidden RDFa, and a POSH <link> in the hero header) curl -sS -L "{delegator-index-html-url}" | grep -i "hasIdentityDelegate" curl -sS -L "{delegate-index-html-url}" | grep -i "onBehalfOf"FULL corroboration requires BOTH sides to declare the relationship — either side alone is a one-sided, unverified claim (matches
verified-identity.ttl:step-verifyDelegationClaims). Verified live 2026-09-13 for the agent/principal pair: both profiles declared the relationship correctly, consistently, across every serialization — and the delegate'sonBehalfOflist may legitimately contain more than one delegator (the agent's own profile lists three).Secondary/optional — remote WebID-TLS verification-service call, presents the delegate's own certificate over mTLS to a verification endpoint, asserting
On-Behalf-Ofthe delegator:curl -iL "https://{Remote-CNAME}:{webid-tls-port}/webid/webid_verify.vsp?callback=webid_result.vsp" \ --cert-type P12 \ -H "On-Behalf-Of: {delegator-webid}" \ --cert "{delegate-webid.p12}" \ --pass "{delegate-webid.p12-pwd}"{webid-tls-port}is the host's mTLS listener (5443on OpenLink ODS/Virtuoso deployments — plain:443typically never issues aCertificateRequest). Empirically confirmed 2026-09-13, this check is WEAKER than the primary one above and should never be used alone: it validates and returns only the presenting certificate's own identity, ignoring whateverOn-Behalf-Ofnames — confirmed by two live tests (self-referential vs. a genuinely different delegator) returning byte-for-byte identical output. It also never populates its ownpku(public-key) field for any identity tested, suggesting it doesn't even complete the canonical dereference-and-match-published-key step for the certificate holder itself, let alone the delegator. Treat a200/code: Successfrom it as proof only that a well-formed certificate was presented — nothing about delegation.Full detail: standalone report
webid-tls-on-behalf-of-delegation-no-effect-incident-report-claude_sonnet_5-1.md, andagent-rdf-memory/preferences.ttlStep 296 /howto/remote-webid-verification-service-obo.ttl.
T8 — did:nostr Identity (create-agent) + WebID Link
Full reference: references/did-nostr.md. Requires Node ≥ 22 (npx, global WebSocket), Python 3 + rdflib, and openssl.
- Elicit:
- Agent directory. It becomes a git repo, and the secret goes in its
git config nostr.privkey. - WebID to link (optional). If one is given, ask "Whose identity is this?":
same(owl:sameAs / alsoKnownAs) oragent(hasIdentityDelegate / onBehalfOf). - Relays to advertise (optional,
wss://). - Extra key storage (optional): macOS Keychain (
-K) and/or an encrypted.p12backup (-X file -p pass). Tell the user the.p12is a backup only and cannot be used for WebID-TLS. - Whether to write the HTTP-resolution copy (
-H <webroot>).
- Agent directory. It becomes a git repo, and the secret goes in its
- Mint + DID side of the link:
To apply options to an identity that already exists, addscripts/generate_nostr_identity.sh -d <dir> [-w <webid> -R same|agent] [-r wss://relay/]... [-K] [-X out.p12 -p <pass>] [-H <webroot>]-E. Built-in gates:- the secret derives the DID key
- the Keychain entry reads back to the same key
- the
.p12's secp256k1 x-coordinate equals the DID key
- WebID side of the link: run T1/T2 with
-N <did> -R <same mode>. Step 7 ofgenerate_identity.shthen gates the link and the Multikey across all 4 representations. - Publish:
- Upload the WebID bundle (T5).
- Serve
<webroot>/.well-known/did/nostr/<hex>.jsonasapplication/did+jsonathttps://<domain>/.well-known/did/nostr/<hex>.json.
- Verify (all resolution options):
Report LINK VERIFIED only when both sides agree. A one-sided claim is NOT VERIFIED and must be reported as such.scripts/resolve_did.py resolve did:nostr:<hex> # offline minimal scripts/resolve_did.py resolve did:nostr:<hex> --domain <domain> # HTTP .well-known scripts/resolve_did.py resolve did:nostr:<hex> --relay wss://relay/ # relay-enhanced (signatures verified) scripts/resolve_did.py link <webid> did:nostr:<hex> --domain <domain> [--relay …] scripts/resolve_did.py prove did:nostr:<hex> --key git:<dir> | keychain:<npub> | p12:<file> # YOUID_P12_PASS for p12 - Secret handling: never print the secret, never put it in the output bundle, and never commit it. The DID document (
agent.did.json) is public and safe to commit.
T7 — Define Identity Concept
Explain semantic web identity concepts using references/identity-ontologies.md:
| Concept | Description |
| WebID | URI-based identity for agents, people, organizations — foundation of WebID-TLS/WebID-OIDC |
| NetID | A WebID bound to an X.509 certificate — the YouID extension's core identity unit |
| X.509 Certificate | Digital certificate binding a public key to a subject (the WebID appears in SAN) |
| FOAF | Friend-of-a-Friend ontology — machine-readable homepages and social networks |
| cert ontology | W3C WebID crypto ontology — RSAPublicKey, modulus, exponent |
| oplcert | OpenLink certificate ontology — Certificate, fingerprint, SAN, IAN, hasKey |
| DPKI | Decentralized Public Key Infrastructure — identity without central authority |
| NI/DI URI | Named Information / Digest Identifier — content-addressed identity URIs |
| owl:sameAs | Identity link between equivalent entities across graphs |
| WebID-TLS | Authentication protocol using X.509 certificates bound to WebIDs |
| WebID-OIDC | Authentication using OpenID Connect with a WebID as subject identifier |
| did:nostr | W3C DID method whose identifier is a Nostr secp256k1 x-only public key (hex); resolvable offline from the key alone |
| npub / nsec | NIP-19 bech32 display encodings of a Nostr public / secret key (the hex is canonical) |
| Multikey | W3C Controlled Identifiers (CID) v1 verification-method type; did:nostr uses publicKeyMultibase = fe70102 + hex |
| alsoKnownAs | DID Core property claiming the same entity — the reciprocal half of a WebID ⇄ did:nostr owl:sameAs link |
Template System Reference
All template files in templates/ use the YouID template syntax:
| Marker | Meaning |
%{key} | Simple substitution — replaced with the value of key (always filled) |
!{key} | Conditional line — the entire line is included only if key is set to a non-empty value |
!!{key} | Conditional block start — text from this line to !!. is included only if key is set |
!!. | Conditional block end — terminates the nearest !!{key} block |
Variable Categories
Full variable reference in references/template-variables.md.
| Category | Variables | Source |
| Identity | subj_name, subj_email, subj_org, subj_country, subj_state, subj_title | User input |
| Certificate | modulus, exponent, fingerprint_hex, fingerprint_256_hex, date_before, date_after, serial, subject, issuer, ca_cert_url, cert_type | Computed from X.509 cert |
| Fingerprint URIs | fingerprint_ni, fingerprint_di, fingerprint_colon, vcard_digest_uri | Computed from fingerprint |
| URLs | prof_url, card_url, cert_url, pubkey_url, pubkey_pem_url, jsonld_*, rdfa_* | Derived from base URL + filenames |
| Conditionals | !{pdp_url}, !{subj_email}, !!{use_opal_widget}, !!{ca_cert_url} | Set/non-set flags |
| Relations | relList, relList_json, relList_html, rel_header_html | Computed from social links |
| OPAL | w_opl_endpoint, w_model, w_module (agent config name), w_funcs, w_assistant, w_prompt1-4 | User config (optional) |
| did:nostr | nostr_did, nostr_npub, nostr_hex, nostr_multibase, nostr_relation_label; flags nostr_same / nostr_agent | Computed from -N / -R |
Pre-Build Check
Before running any generation workflow, you MUST:
- ⛔ Re-read this section of SKILL.md (Order of Operations — the relevant T# workflow).
- ⛔ Load the relevant script — read
scripts/generate_identity.sh(or the appropriate script) to understand its parameters, inputs, and outputs. - ⛔ Read the relevant template files — read each
.tplfile that will be filled so you understand the output structure. - ⛔ Verify openssl is available — required for certificate generation:
which openssl openssl version - ⛔ Verify Python 3 is available — required for template filling:
which python3 - ⛔ If ACME cert type selected: Verify
curlis available andhttps://get.acme.shis reachable:
Ifcurl -fsI https://get.acme.shacme.shis not installed, the script will auto-install it viacurl. If ZeroSSL mode, verify the user has a ZeroSSL API key ready. - ⛔ If ACME cert type selected: Verify the domain resolves:
ACME validation requires the domain to resolve to a publicly accessible IP for HTTP-01 challenges.dig +short <acme-domain> A
Post-Generation Checklist
Before delivering any generated identity to the user:
- All files exist: profile.ttl, profile.jsonld, profile_rdfa.html, certificate.ttl, certificate.jsonld, certificate.rdfa.html, public_key.ttl, public_key.jsonld, public_key.rdfa.html, index.html, vcard.vcf, style.css, cert.pem, cert.crt, cert.p12
- RDF syntax valid: Turtle files parse with
python3 -c "import rdflib; rdflib.Graph().parse('profile.ttl', format='turtle')" - JSON-LD valid:
python3 -c "import json; json.load(open('profile.jsonld'))" - X.509 valid:
openssl x509 -in cert.pem -noout -text - WebID SAN present:
openssl x509 -in cert.pem -noout -ext subjectAltName | grep -i URI - No self-referencing owl:sameAs: no triple where
owl:sameAshas identical subject and object - exponent is typed literal:
cert:exponentinprofile.ttlandpublic_key.ttlmust use"65537"^^xsd:int(typed literal), never bare65537(xsd:integer) — some SPARQL processors require strictxsd:intmatching - exponent is not corrupted:
cert:exponentvalue must be65537(from cert), not"X509v3extensions:"— known bug ingenerate_certificate.sh:107fixed 2026-06-18 (grep -A1replaced withawk '/Exponent:/ {print $2}') - Entity IRIs consistent: the
profile.ttlentity IRIs match the<rel>links inindex.html - Photo accessible: if a photo URL was provided, verify it resolves
- QR code functional: the
index.htmlpage has a QR code pointing to itself - Conditional blocks correct: if OPAL was disabled, verify no OPAL JS is embedded
- Accordion contract: cert
<details>starts closed (openattribute absent), pubkey<details class="nested-details">starts closed, each has<summary>with preview + chevron - Dark mode present:
@media (prefers-color-scheme:dark)rule NOT used (would block manual toggle). Instead[data-theme="dark"]CSS block overrides:rootvariables, with flash-prevention<script>in<head> - Theme toggle implemented:
[data-theme="dark"]CSS block overrides CSS variables, flash-prevention<script>in<head>readslocalStorage('theme')thenprefers-color-scheme, and a sun/moon SVG toggle button is present with click handler that sets/removesdata-themeon<html>and persists tolocalStorage - Platform icons exist: every
src="p_{key}_32.png"in.social-gridhas a corresponding file in the output directory - Platform icons use proper logos: no icon uses
p_none.pngunless a platform-specific icon was not discoverable after searching the platform's brand page - No external framework: no
bootstrap,jquery, or other framework imports in the HTML body - Basic WebID Test PASS — AUTO-GATED in Step 5 of
generate_identity.sh. The orchestrator extracts modulus + exponent fromcert.p12and cross-referencesprofile.ttl,profile.jsonld, andindex.html(RDFa) usingrdflibandHTMLParser. Generation is blocked with exit code 1 on any mismatch. Manual check no longer required. - Delegation & Cert:Key Consistency Test PASS — AUTO-GATED in Step 6 of
generate_identity.sh. If any file containsoplcert:hasIdentityDelegateoroplcert:onBehalfOf, the gate verifies identical triples acrossprofile.ttl,profile.jsonld,profile_rdfa.html, andindex.html(embedded JSON-LD + RDFa rel/property attrs), AND that every named delegate has its owncert:key(modulus+exponent) republished consistently across the same four files, with matching modulus values wherever inlined. Skips cleanly if no delegation triples are present. Blocks generation on failure. did:nostr Link Consistency Test PASS — AUTO-GATED in Step 7 of
generate_identity.shwhen-Nis given (scripts/nostr_link_gate.py). It checks, acrossprofile.ttl,profile.jsonld,profile_rdfa.html, andindex.html(embedded JSON-LD + Turtle, plus the RDFa layer on its own), that:- the DID is an on-curve key
publicKeyMultibase==fe70102+ hex- the npub decodes to the same key
- exactly the expected relation is present (
owl:sameAsforsame,oplcert:hasIdentityDelegateforagent)
Blocks generation on failure.
- did:nostr reciprocal side published (T8):
agent.did.jsoncarriesalsoKnownAs(same) oroplcert:onBehalfOf(agent), andscripts/resolve_did.py linkreports LINK VERIFIED once both documents are live - Hero badges rendered: "✓ Verified WebID", subject name, email, org all visible
- Social
owl:sameAspresent:profile.ttlandprofile.jsonldcontainowl:sameAsentries for each social platform URL collected from the user (not just profile-document equivalences) <link rel="me">in head:index.htmlhas<link rel="me">tags for each social platform URL- Social grid populated:
index.html's.social-griddiv contains platform-icon<a>tags withrel="me"for each social URL - No empty social-grid: if social links were collected, verify
.social-gridis non-empty; if none were collected, the section should be conditionally hidden
ACME-Specific Post-Generation Checks
- CA-signed certificate chain valid (ACME modes only):
openssl verify -CAfile <(openssl x509 -in ca.cer) cert.pem - URI SAN present in ACME cert:
openssl x509 -in cert.pem -noout -ext subjectAltName | grep 'URI:' - DNS SAN matches domain:
openssl x509 -in cert.pem -noout -ext subjectAltName | grep 'DNS.'matches the ACME domain ca_cert_urlpopulated incert_data.json: checked withpython3 -c "import json; d=json.load(open('cert_data.json')); assert d['ca_cert_url'], 'missing ca_cert_url'"ca.cerexists: For ACME modes, the CA certificate file must be present in the output directorycert_typeincert_data.json: set toletsencryptorzerossl(notself-signed)- Issuer is CA (not self-signed):
issuerfield incert_data.jsonshould reference the CA, not the subject - Template conditionals active: verify
profile.ttlandprofile.jsonldcontainoplcert:IANandxhv:alternateforca_cert_url
Operational Rules
- Always use the scripts, never hand-write artifacts. The scripts ensure deterministic output that matches the templates. LLM-generated Turtle/JSON-LD/HTML drifts from the template contracts.
- Never guess or fabricate certificate data. Modulus, exponent, fingerprints, and dates MUST come from the actual generated certificate. Run the script to generate the cert, then extract these values from
cert_data.json. - Never guess or fabricate NI/DI URIs. Compute them from the actual certificate fingerprint using
compute_fingerprints.sh. - WebID SAN is mandatory. Every generated X.509 certificate MUST have the user's WebID URI as a Subject Alternative Name. This is what binds the WebID to the cryptographic credential.
- One identity per run. Each skill invocation generates one NetID. For multiple identities, run the workflow multiple times.
- Social relations are
owl:sameAstargets. Each social profile link becomes anowl:sameAs(Turtle/JSON-LD) and<link rel="me">(HTML) reference. These MUST resolve to the user's profile on that platform. - Multiple output formats are not optional. Always generate all 6 RDF formats (Turtle, JSON-LD, RDFa HTML for profile, certificate, and public_key) + index.html + vcard.vcf by default. Skip only if the user explicitly requests a subset.
Platform Icon Reference
When generating index.html, every social/profile platform URL in owl:sameAs / schema:sameAs needs a branded 32×32 icon in .social-grid. Icon files follow the naming convention p_{key}_32.png.
| Platform | href pattern | Icon file | --social-color | Brand page for logo download |
https://linkedin.com/in/{user} | p_linkedin_32.png | #0077b5 | N/A — use existing asset | |
| X/Twitter | https://x.com/{user} | p_twitter_32.png | #000000 | N/A — use existing asset |
| Substack | https://substack.com/@{user} | p_substack_32.png | #FF6719 | https://substack.com/brand |
| Mastodon | https://mastodon.social/@{user} | p_mastodon_32.png | #6364ff | N/A — use existing asset |
| Bluesky | https://bsky.app/profile/{user} | p_bluesky_32.png | #1185fe | N/A — use existing asset |
| Threads | https://www.threads.net/@{user} | p_threads_32.png | #000000 | N/A — use existing asset |
| GitHub | https://github.com/{user} | p_github_32.png | #333333 | N/A — use existing asset |
| Linktree | https://linktr.ee/{user} | p_linktree_32.png | #39e09b | N/A — use existing asset |
| ID.MyOpenLink.NET | https://id.myopenlink.net/~{name}/Public/ | p_myopenlink_32.png | #2563eb | N/A — use existing asset |
| Carrd | https://{user}.carrd.co/ | p_carrd_32.png | #2eaadc | N/A — use existing asset |
| Glitch | https://glitch.com/~{user} | p_glitch_32.png | #65387d | N/A — use existing asset |
https://facebook.com/{user} | p_facebook_32.png | #1877f2 | N/A — use existing asset | |
https://www.instagram.com/{user} | p_insta_32.png | #e4405f | N/A — use existing asset | |
| TikTok | https://www.tiktok.com/@{user} | p_tiktok_32.png | #000000 | N/A — use existing asset |
| RSS | Feed URL (e.g. podcasts RSS) | p_rss_32.png | #ff6600 | N/A — use existing asset |
| Atom | Feed URL (e.g. podcasts Atom) | p_atom_32.png | #ff6600 | N/A — use existing asset |
mailto:{email} | p_email_32.png | #666666 | https://www.flaticon.com/free-icons/email |
Icon Provisioning Rules
- Prefer existing assets — if
p_{key}_32.pngalready exists inassets/, copy it to the output directory. No download needed. - Download from brand page — for new platforms, visit the platform's official brand page (e.g.
https://{platform}.com/brand) and download their logo. If the brand page URL isn't known, search for{platform} brand assets. - Resize to 32×32 — use macOS
sips:
For PNG downloads at larger sizes,curl -sL -o /tmp/icon.png "{logo-url}" && sips -z 32 32 /tmp/icon.png --out "assets/p_{key}_32.png"sips -z 32 32handles downscaling. - Fall back to
p_none.png— if no official logo can be found, use the generic placeholder. - Copy to output directory — after provisioning/resizing, copy
p_{key}_32.pngfromassets/to the credential output directory alongsideindex.html.
Social Cross-Reference Pattern
When adding social platform URLs to a credential set, ALL four profile files must be updated consistently:
| File | What to add |
profile.ttl | owl:sameAs <{url}> on the #netid entity (document profiles + all social) + schema:sameAs <{url}> (social only) |
profile.jsonld | "owl:sameAs": [{"@id": "{url}"}] and "schema:sameAs": [{"@id": "{url}"}] on #netid |
index.html | <link rel="me" href="{url}" title="{Platform}" /> in <head> + <a href="{url}" target="_blank" rel="me" title="{Platform}" style="--social-color:{color}"><img src="p_{key}_32.png" alt="{Platform}" /></a> in .social-grid |
profile_rdfa.html | <link rel="me" href="{url}" title="{Platform}" /> in ${RelMeAuth Relations} section |
The #netid entity IRI base: https://{host}/.../index.html#netid
Resources
Directory Structure
youid/
├── SKILL.md # This file
├── references/
│ ├── identity-ontologies.md # FOAF, cert, oplcert, schema.org, X.509 vocab reference
│ ├── template-variables.md # Complete variable reference (%{key}, !{key}, !!{key})
│ ├── verification-queries.md # SPARQL queries for WebID profile verification
│ ├── upload-backends.md # curl commands for WebDAV/LDP uploads
│ ├── acme-workflows.md # ACME certificate workflow reference (LE, ZeroSSL)
│ └── did-nostr.md # did:nostr (T8): spec facts, link modes, key custody, verification
├── templates/
│ ├── profile.ttl.tpl # Turtle profile document template
│ ├── profile.jsonld.tpl # JSON-LD profile document template
│ ├── profile_rdfa.html.tpl # RDFa HTML profile page template
│ ├── prof_microdata.tpl # Microdata profile (legacy/alt)
│ ├── prof_rdfa.tpl # Legacy RDFa profile template (pre-HTML5)
│ ├── certificate.ttl.tpl # Certificate metadata in Turtle template
│ ├── certificate.jsonld.tpl # Certificate metadata in JSON-LD template
│ ├── certificate.rdfa.html.tpl # Certificate metadata in RDFa HTML template
│ ├── public_key.ttl.tpl # Public key in Turtle template
│ ├── public_key.jsonld.tpl # Public key in JSON-LD template
│ ├── public_key.rdfa.html.tpl # Public key in RDFa HTML template
│ ├── index.html.tpl # Identity card HTML template — Default style: gradient hero, compact layout (accordion-based, no-JS, dark-mode, with OPAL widget)
│ ├── index_premium.html.tpl # Identity card HTML template — Premium style: white hero, gold accents, credential badge bar, clean cards
│ ├── index_dark.html.tpl # Identity card HTML template — Dark style: dark hero, circular photo, question chips, dual CTA
│ ├── vcard.vcf.tpl # vCard VCF template
│ └── style.css # Card CSS
├── scripts/
│ ├── generate_certificate.sh # X.509 cert generation (self-signed or ACME-signed)
│ ├── acme_helper.sh # ACME.sh orchestration (install, CSR, sign, download)
│ ├── compute_fingerprints.sh # Compute SHA-1/SHA-256 fingerprints and NI/DI URIs
│ ├── template_fill.py # Template engine (%{key}, !{key}, !!{key} substitution)
│ ├── generate_identity.sh # Orchestrator: cert → variables → template fill → bundle
│ ├── verify_webid.sh # Fetch and SPARQL-verify a remote WebID profile
│ ├── generate_nostr_identity.sh # T8: did:nostr via create-agent + link/relays/Keychain/.p12/.well-known
│ ├── nostr_link_gate.py # Step 7 gate: WebID ⇄ did:nostr link + Multikey consistency
│ ├── resolve_did.py # did:nostr + WebID resolver; two-sided link check; proof of control
│ ├── nostr_crypto.py # stdlib secp256k1 / BIP-340 / bech32 / NIP-01 helpers (+ selftest)
│ └── nostr_relay.mjs # dependency-free NIP-01 relay client (Node ≥ 22)
└── assets/ # Static assets for identity card HTML
├── style.css # Card CSS (also in templates/)
├── opal.js # OPAL widget (basic) — see extension src/tpl/
├── opalx.js # OPALX widget (enhanced) — see extension src/tpl/
├── auth.js # Bearer auth module — see extension src/tpl/
├── win.js # Window management for OPAL — see extension src/tpl/
├── qrcode.js # QR code library — see extension src/tpl/
├── style_opal.css # OPAL widget CSS — see extension src/tpl/
├── solid-client-authn.bundle.js # Solid OIDC client bundle (Solid POD auth)
├── youid_logo-35px.png # YouID logo
├── addrbook.png # Address book icon
├── lock.png # Lock icon
├── chatbot-32px.png # Chatbot icon
├── photo_130x145.jpg # Default photo placeholder
├── login.svg # Login icon
├── logout.svg # Logout icon
├── museo-500-webfont.eot # Museo 500 font (EOT)
├── museo-500-webfont.ttf # Museo 500 font (TTF)
├── museo-500-webfont.woff # Museo 500 font (WOFF)
├── p_linkedin_32.png # Social: LinkedIn
├── p_twitter_32.png # Social: X/Twitter
├── p_substack_32.png # Social: Substack
├── p_github_32.png # Social: GitHub
├── p_facebook_32.png # Social: Facebook
├── p_mastodon_32.png # Social: Mastodon
├── p_bluesky_32.png # Social: Bluesky
├── p_insta_32.png # Social: Instagram
├── p_threads_32.png # Social: Threads
├── p_tiktok_32.png # Social: TikTok
├── p_atom_32.png # Feed: Atom
├── p_rss_32.png # Feed: RSS
├── p_opml_32.png # Feed: OPML
├── p_myopenlink_32.png # Social: myopenlink
├── p_linktree_32.png # Link: Linktree
├── p_carrd_32.png # Link: Carrd
├── p_disha_32.png # Link: Disha
├── p_glitch_32.png # Link: Glitch
├── p_cal.com_32.png # Calendar: Cal.com
├── p_email_32.png # Email (flaticon envelope icon)
└── p_none.png # Default/no platform iconDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

