agentleFS
OpenLinkSoftwareOpenLinkSoftware/ai-agent-skills/youid/SKILL.md

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.

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
  1. Distinctive:

    Use this skill whenever the user wants to:

    line 73
  2. Distinctive:

    When the user explicitly invokes the YouID skill by name or a clear identity-generation trigger, the skill operates in Strict Harness Mode.

    line 118
  3. Distinctive:

    Every generated artifact must have valid RDF or HTML syntax.

    line 122
  4. Very specific:

    Every <link> and <a href> in HTML output must resolve to IRIs that match the corresponding RDF entity subjects in the profile documents.

    line 123
  5. Distinctive:

    No external CSS or JS frameworks (Bootstrap, jQuery) — pure inline CSS

    line 138
  6. Distinctive:

    Before delivering any output to the user, the following MUST pass:

    line 144
  7. Distinctive:

    All artifact files exist in the output directory

    line 148
  8. Distinctive:

    Default storage URL (optional, PIM)

    line 183
  9. Distinctive:

    Ask: *"What type of X.509 certificate?

    line 189
  10. Very specific:

    If letsencrypt or zerossl selected, collect ACME-specific parameters:

    line 190
  11. Distinctive:

    Use Let's Encrypt staging?

    line 193
  12. Very specific:

    If zerossl, ask: *"What is your ZeroSSL API key?

    line 194
  13. Distinctive:

    Elicit template style: after collecting parameters:

    line 196
  14. Distinctive:

    Ask: *"Which profile page style would you like?

    line 197
  15. Distinctive:

    Ask: *"What URL should we use for your profile photo?"* (or skip to use the default placeholder)

    line 198
  16. Distinctive:

    If Dark style selected, collect a bio summary: *"Write a short bio or summary about the subject for the profile page."*

    line 199
  17. Distinctive:

    If certificate validity was not provided, ask: *"How many years should the X.509 certificate be valid?

    line 202
  18. Very specific:

    Generate certificate: Run scripts/generate_identity.sh with the collected parameters (or scripts/generate_certificate.sh directly).

    line 204
  19. Very specific:

    Fill templates: Run scripts/generate_identity.sh with:

    line 211
  20. Distinctive:

    Check entity IRIs match between files

    line 234
  21. Distinctive:

    Ask: *"Self-signed, or CA-signed via Let's Encrypt/ZeroSSL?"*

    line 264
  22. Very specific:

    If ACME: collect --acme-domain (parsed from WebID if omitted), --acme-email, and for ZeroSSL --zerossl-api-key

    line 265
  23. Very specific:

    Run scripts/generate_certificate.sh with positional args (self-signed) or named args (--mode, --common-name, etc.) to produce .pem, .crt, …

    line 266
  24. Very specific:

    Run scripts/verify_webid.sh or use URIBurner SPARQL endpoint

    line 274
  25. Very specific:

    Use curl PUT/PROPPATCH commands from references/upload-backends.md

    line 291
  26. Distinctive:

    Verify uploads with HEAD requests

    line 293
  27. Very specific:

    If provided, oplcert:onBehalfOf will be added to the delegate's local files too

    line 321
  28. Distinctive:

    If not provided, only the delegator's files are modified

    line 322
  29. Very specific:

    WebID side of the link: run T1/T2 with -N <did> -R <same mode>.

    line 437
  30. Very specific:

    Secret handling: never print the secret, never put it in the output bundle, and never commit it.

    line 450
  31. Very specific:

    All template files in templates/ use the YouID template syntax:

    line 476
  32. Distinctive:

    Before running any generation workflow, you MUST:

    line 502
  33. Very specific:

    If acme.sh is not installed, the script will auto-install it via curl.

    line 520
  34. Distinctive:

    If ZeroSSL mode, verify the user has a ZeroSSL API key ready.

    line 521
  35. Distinctive:

    Before delivering any generated identity to the user:

    line 530
  36. Distinctive:

    WebID SAN is mandatory: Every generated X.509 certificate MUST have the user's WebID URI as a Subject Alternative Name.

    line 585
  37. Very specific:

    Multiple output formats are not optional: Always generate all 6 RDF formats (Turtle, JSON-LD, RDFa HTML for profile, certificate, and …

    line 588
  38. Very specific:

    When generating index.html, every social/profile platform URL in owl:sameAs / schema:sameAs needs a branded 32×32 icon in .social-grid.

    line 592
  39. Distinctive:

    When adding social platform URLs to a credential set, ALL four profile files must be updated consistently:

    line 628

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:sameAs platform 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; --warn or amber for unverified states. Never hardcode hex values for trust-signal colours.
  • RDFa HTML is both data and display — the profile_rdfa.html output 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 .p12 backup, and a .well-known HTTP-resolution copy
  • generate_identity.sh -N <did:nostr|npub|hex> -R same|agent adds the WebID side of the link, plus a sec:Multikey, to profile.ttl, profile.jsonld, and the RDFa in profile_rdfa.html/index.html. It also adds a Nostr Identity card section (all 3 styles) and a vCard nostr: 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 exempts did:nostr: delegates from the RSA cert:key rule
  • New scripts/resolve_did.py: did:nostr resolution (HTTP .well-known / offline minimal / relay-enhanced, with every relay event signature-checked), two-sided link verification, and prove proof-of-control from the git, Keychain, or .p12 key
  • New scripts/nostr_crypto.py (stdlib secp256k1 + BIP-340 + bech32; selftest runs the BIP-340 vectors) and scripts/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 under set -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.sh with --mode self-signed|letsencrypt|zerossl flag
  • Extended generate_identity.sh with -M <cert_type>, -D <domain>, -Z <zerossl_key> flags
  • Created references/acme-workflows.md: ACME reference documentation
  • Added ca_cert_url and cert_type fields to cert_data.json output (enables !!{ca_cert_url} template conditionals)
  • Updated T1/T2/T3 workflows with cert type elicitation steps
  • ACME mode auto-installs acme.sh if missing; fetches ZeroSSL EAB credentials via API

2026-06-18 Updates:

  • Fixed exponent extraction bug in generate_certificate.sh:107 — grep -A1 captured X509v3extensions: instead of 65537. Replaced with awk '/Exponent:/ {print $2}'.
  • Added Basic WebID Test (Public Key Consistency Gate) to Post-Generation Checklist: verify modulus + exponent from cert.p12 match index.html, profile.ttl, and profile.jsonld.
  • Added exponent format requirement: cert:exponent must use "65537"^^xsd:int typed 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

TriggerAction
"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

SettingDefaultDescription
RSA key size2048RSA key pair size in bits
Certificate validity365 days (1 year)Self-signed cert validity period; elicited from user via T1/T3
Digest algorithmsha256Hash for certificate fingerprint
Template enginescripts/template_fill.pyPython script for %{key}/!{key}/!!{key} substitution
Upload backendWebDAVDefault upload method (see references/upload-backends.md)
Output directory./youid-output/Generated files land here
Bundle formatPlain directoryOptionally ZIP with zip -r
Certificate typeself-signedCertificate signing: self-signed, letsencrypt, or zerossl. Elicited from user via T1/T2/T3 with ACME-specific parameters
ACME domainParsed from WebIDDomain used for ACME domain validation (extracted from WebID URI hostname)
ACME stagingfalseUse Let's Encrypt staging endpoint for testing (--acme-staging)
ACME emailFrom --acme-email or email paramContact email for ACME account registration and renewal
ZeroSSL EABRequired for zerossl modeFetch 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

  1. Every generated artifact must have valid RDF or HTML syntax.
  2. Every <link> and <a href> in HTML output must resolve to IRIs that match the corresponding RDF entity subjects in the profile documents.
  3. The X.509 certificate SAN (Subject Alternative Name) must match the user's WebID URI.
  4. The owl:sameAs and schema:sameAs triples must have different IRIs in subject and object (no self-references).
  5. Generated IRIs must use the resolver pattern: https://linkeddata.uriburner.com/describe/?url={url-encoded-iri} for entity hyperlinks.
  6. All schema: properties and types must be real schema.org terms — no fabricated IRIs.
  7. 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"> without open attribute)
    • 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-color CSS 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 @media rule would block the manual toggle on dark-system devices. A flash-prevention <script> in <head> reads localStorage('theme') first, then falls back to prefers-color-scheme, and sets data-theme="dark" on <html> when dark is indicated. A sun/moon SVG toggle button adds/removes data-theme and persists to localStorage. Removing data-theme always 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 --style flag (premium, dark, or omitted for default) selects which template is used. The output file is always index.html regardless of selected template.

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:sameAs has 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.p12 matches index.html, profile.ttl, and profile.jsonld — automated in Step 5 of generate_identity.sh, blocks generation on failure
  • Delegation & Cert:Key Consistency Gate: If any output file contains oplcert:hasIdentityDelegate or oplcert:onBehalfOf triples, they MUST match identically across profile.ttl, profile.jsonld, profile_rdfa.html, and index.html (all 4 representations: POSH, embedded JSON-LD, embedded Turtle, hidden RDFa) — automated in Step 6 of generate_identity.sh, blocks generation on failure. As of 2026-09-13, the gate also requires every delegate named in hasIdentityDelegate to have its own cert: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. See agent-rdf-memory/howto/webid-tls-on-behalf-of-delegation-no-effect-incident-report... and preferences.ttl for the live-verified finding this gate now prevents from recurring.

Execution Routing

PriorityMethodWhen
1scripts/generate_identity.shLocal cert generation + template fill (deterministic, no LLM drift)
2scripts/verify_webid.sh + curlWebID verification via SPARQL against URIBurner
3URIBurner REST (Demo.demo.execute_spasql_query)WebID verification via SPASQL endpoint
4curl (WebDAV/LDP PUT)Upload identity documents to storage backends
5LLM-guidedCollect 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 letsencrypt or -M zerossl for 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

  1. 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)
  • Social relation links (optional, list of platform URLs)
  • Bio summary (optional, free-text, for dark template bio section; set as subj_summary in 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 assume same; see references/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 letsencrypt or zerossl selected, 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)"
  1. 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_style in extra data JSON: "premium", "dark", or omit for default
    • Set photo_url and optionally subj_summary in extra data JSON if provided
    • If certificate validity was not provided, ask: "How many years should the X.509 certificate be valid? (default: 1)"
  2. Generate certificate: Run scripts/generate_identity.sh with the collected parameters (or scripts/generate_certificate.sh directly). For ZeroSSL mode, the script auto-installs acme.sh if missing and fetches EAB credentials. Pass validity as the 9th positional argument or --validity-days for named-arg mode. This creates:
    • cert.pem — PEM-encoded X.509 certificate (fullchain for ACME modes)
    • cert.crt — DER-encoded X.509 certificate
    • cert.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)
  3. Fill templates: Run scripts/generate_identity.sh with:

    • 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 --style flag or profile_style in 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 document
    • profile_rdfa.html — RDFa HTML profile page
    • certificate.ttl — Certificate metadata in Turtle
    • certificate.jsonld — Certificate metadata in JSON-LD
    • certificate.rdfa.html — Certificate metadata in RDFa HTML
    • public_key.ttl — Public key information in Turtle
    • public_key.jsonld — Public key information in JSON-LD
    • public_key.rdfa.html — Public key information in RDFa HTML
    • vcard.vcf — vCard VCF contact card
    • style.css — Card styling
    • index.html — Identity card HTML page (using selected template, with conditional OPAL widget)
  4. Verify output: Validate each generated artifact.
    • Parse .ttl files with rdflib
    • Validate X.509 cert with openssl
    • Check entity IRIs match between files
  5. 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 (bearer or oauth)
  • API key (if bearer auth)
  • Widget mode (opal basic or opalx enhanced 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 .json suffix 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

  1. 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
  2. Run scripts/generate_certificate.sh with positional args (self-signed) or named args (--mode, --common-name, etc.) to produce .pem, .crt, .p12, cert_data.json
  3. Display fingerprint, modulus, exponent, issuer, validity dates, and CA cert URL (ACME) to the user
  4. Deliver the certificate files

T4 — Verify WebID Profile

  1. Fetch the WebID URL using curl
  2. Determine the RDF serialization format (content-type negotiation or heuristic)
  3. Run scripts/verify_webid.sh or use URIBurner SPARQL endpoint
  4. Report: name(s), public keys (modulus, exponent, fingerprint), certificates (including CA signer for ACME certs), delegates, storage locations, inbox, outbox, foaf:knows relations, email
  5. For ACME-signed certificates, additionally verify:
    • Issuer DN references the CA, not the subject
    • oplcert:IAN triple 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

  1. Determine the upload backend from user's destination URL:
    • OpenLink WebDAV (DAV home)
    • OpenLink LDP
    • Generic LDP over TLS
    • WebDAV over HTTPS
  2. Use curl PUT/PROPPATCH commands from references/upload-backends.md
  3. Upload all files from the output directory (5–15 files depending on config; ACME modes also upload ca.cer)
  4. Verify uploads with HEAD requests

T6 — Generate On-Behalf-Of Delegation

Workflow-start elicitation (MUST collect before any generation):

  1. 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
  2. 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
  3. 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"
  4. 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
  5. 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:onBehalfOf will 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:hasIdentityDelegate only (grants authority)
  • Delegate's documents — oplcert:onBehalfOf only (acknowledges authority) This differs from SPARQL UPDATE deployment where both triples may reside in a single graph (see references/delegation.md).
  1. Add oplcert:hasIdentityDelegate to the delegator's local profile files — every RDF representation MUST contain the triple:

    FileRepresentationWhat to add
    profile.ttlTurtleoplcert:hasIdentityDelegate <delegate-webid> . on the #netid entity
    profile.jsonldJSON-LD"oplcert:hasIdentityDelegate": { "@id": "<delegate-webid>" } in the #netid graph entry
    profile_rdfa.htmlRDFa + JSON-LD<div rel="oplcert:hasIdentityDelegate" resource="<delegate-webid>"></div> AND add to embedded JSON-LD graph
    index.htmlPOSH (hero <header>)<link property="oplcert:hasIdentityDelegate" href="<delegate-webid>" /> after the existing <link property="cert:key">
    index.htmlEmbedded JSON-LDGraph entry with "oplcert:hasIdentityDelegate": { "@id": "<delegate-webid>" } — also add @type: "foaf:Agent" and schema:additionalType: "Delegator"
    index.htmlEmbedded Turtleoplcert:hasIdentityDelegate <delegate-webid> . on the #netid entity
    index.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:

FileRepresentationWhat to add
profile.ttlTurtle<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.jsonldJSON-LDSame shape as profile.ttl, in @graph
profile_rdfa.htmlRDFa<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.htmlEmbedded 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.

  1. If delegate directory provided, add oplcert:onBehalfOf to the delegate's local profile files using the same index.html coverage:

    FileRepresentationWhat to add
    profile.ttlTurtleoplcert:onBehalfOf <delegator-webid> . on the #netid entity
    profile.jsonldJSON-LD"oplcert:onBehalfOf": { "@id": "<delegator-webid>" } in the #netid graph entry
    profile_rdfa.htmlRDFa + JSON-LD<div rel="oplcert:onBehalfOf" resource="<delegator-webid>"></div> AND add to embedded JSON-LD graph
    index.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 #netid entry — add in both the foaf:Agent and schema:Person graph entries if both exist
    index.htmlEmbedded Turtleoplcert:onBehalfOf <delegator-webid> . on the #netid entity
    index.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>
  2. Generate declarativeNetRequest rule — produce delegation-rule.json for the delegate's browser (injects On-Behalf-Of header):
    {
      "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-Of HTTP 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.
  3. 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]"
  4. Verify after upload — the remote OBO test is the SAME corroboration the local whoami identity-verification flow does (agent-rdf-memory/howto/verified-identity.ttl Steps 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's onBehalfOf list 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-Of the 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 (5443 on OpenLink ODS/Virtuoso deployments — plain :443 typically never issues a CertificateRequest). 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 whatever On-Behalf-Of names — confirmed by two live tests (self-referential vs. a genuinely different delegator) returning byte-for-byte identical output. It also never populates its own pku (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 a 200/code: Success from 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, and agent-rdf-memory/preferences.ttl Step 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.

  1. 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) or agent (hasIdentityDelegate / onBehalfOf).
    • Relays to advertise (optional, wss://).
    • Extra key storage (optional): macOS Keychain (-K) and/or an encrypted .p12 backup (-X file -p pass). Tell the user the .p12 is a backup only and cannot be used for WebID-TLS.
    • Whether to write the HTTP-resolution copy (-H <webroot>).
  2. Mint + DID side of the link:
    scripts/generate_nostr_identity.sh -d <dir> [-w <webid> -R same|agent] [-r wss://relay/]... [-K] [-X out.p12 -p <pass>] [-H <webroot>]
    To apply options to an identity that already exists, add -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
  3. WebID side of the link: run T1/T2 with -N <did> -R <same mode>. Step 7 of generate_identity.sh then gates the link and the Multikey across all 4 representations.
  4. Publish:
    • Upload the WebID bundle (T5).
    • Serve <webroot>/.well-known/did/nostr/<hex>.json as application/did+json at https://<domain>/.well-known/did/nostr/<hex>.json.
  5. Verify (all resolution options):
    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
    Report LINK VERIFIED only when both sides agree. A one-sided claim is NOT VERIFIED and must be reported as such.
  6. 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:

ConceptDescription
WebIDURI-based identity for agents, people, organizations — foundation of WebID-TLS/WebID-OIDC
NetIDA WebID bound to an X.509 certificate — the YouID extension's core identity unit
X.509 CertificateDigital certificate binding a public key to a subject (the WebID appears in SAN)
FOAFFriend-of-a-Friend ontology — machine-readable homepages and social networks
cert ontologyW3C WebID crypto ontology — RSAPublicKey, modulus, exponent
oplcertOpenLink certificate ontology — Certificate, fingerprint, SAN, IAN, hasKey
DPKIDecentralized Public Key Infrastructure — identity without central authority
NI/DI URINamed Information / Digest Identifier — content-addressed identity URIs
owl:sameAsIdentity link between equivalent entities across graphs
WebID-TLSAuthentication protocol using X.509 certificates bound to WebIDs
WebID-OIDCAuthentication using OpenID Connect with a WebID as subject identifier
did:nostrW3C DID method whose identifier is a Nostr secp256k1 x-only public key (hex); resolvable offline from the key alone
npub / nsecNIP-19 bech32 display encodings of a Nostr public / secret key (the hex is canonical)
MultikeyW3C Controlled Identifiers (CID) v1 verification-method type; did:nostr uses publicKeyMultibase = fe70102 + hex
alsoKnownAsDID 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:

MarkerMeaning
%{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.

CategoryVariablesSource
Identitysubj_name, subj_email, subj_org, subj_country, subj_state, subj_titleUser input
Certificatemodulus, exponent, fingerprint_hex, fingerprint_256_hex, date_before, date_after, serial, subject, issuer, ca_cert_url, cert_typeComputed from X.509 cert
Fingerprint URIsfingerprint_ni, fingerprint_di, fingerprint_colon, vcard_digest_uriComputed from fingerprint
URLsprof_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
RelationsrelList, relList_json, relList_html, rel_header_htmlComputed from social links
OPALw_opl_endpoint, w_model, w_module (agent config name), w_funcs, w_assistant, w_prompt1-4User config (optional)
did:nostrnostr_did, nostr_npub, nostr_hex, nostr_multibase, nostr_relation_label; flags nostr_same / nostr_agentComputed from -N / -R

Pre-Build Check

Before running any generation workflow, you MUST:

  1. ⛔ Re-read this section of SKILL.md (Order of Operations — the relevant T# workflow).
  2. ⛔ Load the relevant script — read scripts/generate_identity.sh (or the appropriate script) to understand its parameters, inputs, and outputs.
  3. ⛔ Read the relevant template files — read each .tpl file that will be filled so you understand the output structure.
  4. ⛔ Verify openssl is available — required for certificate generation:
    which openssl
    openssl version
  5. ⛔ Verify Python 3 is available — required for template filling:
    which python3
  6. ⛔ If ACME cert type selected: Verify curl is available and https://get.acme.sh is reachable:
    curl -fsI https://get.acme.sh
    If acme.sh is not installed, the script will auto-install it via curl. If ZeroSSL mode, verify the user has a ZeroSSL API key ready.
  7. ⛔ If ACME cert type selected: Verify the domain resolves:
    dig +short <acme-domain> A
    ACME validation requires the domain to resolve to a publicly accessible IP for HTTP-01 challenges.

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:sameAs has identical subject and object
  • exponent is typed literal: cert:exponent in profile.ttl and public_key.ttl must use "65537"^^xsd:int (typed literal), never bare 65537 (xsd:integer) — some SPARQL processors require strict xsd:int matching
  • exponent is not corrupted: cert:exponent value must be 65537 (from cert), not "X509v3extensions:" — known bug in generate_certificate.sh:107 fixed 2026-06-18 (grep -A1 replaced with awk '/Exponent:/ {print $2}')
  • Entity IRIs consistent: the profile.ttl entity IRIs match the <rel> links in index.html
  • Photo accessible: if a photo URL was provided, verify it resolves
  • QR code functional: the index.html page 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 (open attribute 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 :root variables, with flash-prevention <script> in <head>
  • Theme toggle implemented: [data-theme="dark"] CSS block overrides CSS variables, flash-prevention <script> in <head> reads localStorage('theme') then prefers-color-scheme, and a sun/moon SVG toggle button is present with click handler that sets/removes data-theme on <html> and persists to localStorage
  • Platform icons exist: every src="p_{key}_32.png" in .social-grid has a corresponding file in the output directory
  • Platform icons use proper logos: no icon uses p_none.png unless 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 from cert.p12 and cross-references profile.ttl, profile.jsonld, and index.html (RDFa) using rdflib and HTMLParser. 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 contains oplcert:hasIdentityDelegate or oplcert:onBehalfOf, the gate verifies identical triples across profile.ttl, profile.jsonld, profile_rdfa.html, and index.html (embedded JSON-LD + RDFa rel/property attrs), AND that every named delegate has its own cert: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.sh when -N is given (scripts/nostr_link_gate.py). It checks, across profile.ttl, profile.jsonld, profile_rdfa.html, and index.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:sameAs for same, oplcert:hasIdentityDelegate for agent)

    Blocks generation on failure.

  • did:nostr reciprocal side published (T8): agent.did.json carries alsoKnownAs (same) or oplcert:onBehalfOf (agent), and scripts/resolve_did.py link reports LINK VERIFIED once both documents are live
  • Hero badges rendered: "✓ Verified WebID", subject name, email, org all visible
  • Social owl:sameAs present: profile.ttl and profile.jsonld contain owl:sameAs entries for each social platform URL collected from the user (not just profile-document equivalences)
  • <link rel="me"> in head: index.html has <link rel="me"> tags for each social platform URL
  • Social grid populated: index.html's .social-grid div contains platform-icon <a> tags with rel="me" for each social URL
  • No empty social-grid: if social links were collected, verify .social-grid is 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_url populated in cert_data.json: checked with python3 -c "import json; d=json.load(open('cert_data.json')); assert d['ca_cert_url'], 'missing ca_cert_url'"
  • ca.cer exists: For ACME modes, the CA certificate file must be present in the output directory
  • cert_type in cert_data.json: set to letsencrypt or zerossl (not self-signed)
  • Issuer is CA (not self-signed): issuer field in cert_data.json should reference the CA, not the subject
  • Template conditionals active: verify profile.ttl and profile.jsonld contain oplcert:IAN and xhv:alternate for ca_cert_url

Operational Rules

  1. 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.
  2. 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.
  3. Never guess or fabricate NI/DI URIs. Compute them from the actual certificate fingerprint using compute_fingerprints.sh.
  4. 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.
  5. One identity per run. Each skill invocation generates one NetID. For multiple identities, run the workflow multiple times.
  6. Social relations are owl:sameAs targets. Each social profile link becomes an owl:sameAs (Turtle/JSON-LD) and <link rel="me"> (HTML) reference. These MUST resolve to the user's profile on that platform.
  7. 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.

Platformhref patternIcon file--social-colorBrand page for logo download
LinkedInhttps://linkedin.com/in/{user}p_linkedin_32.png#0077b5N/A — use existing asset
X/Twitterhttps://x.com/{user}p_twitter_32.png#000000N/A — use existing asset
Substackhttps://substack.com/@{user}p_substack_32.png#FF6719https://substack.com/brand
Mastodonhttps://mastodon.social/@{user}p_mastodon_32.png#6364ffN/A — use existing asset
Blueskyhttps://bsky.app/profile/{user}p_bluesky_32.png#1185feN/A — use existing asset
Threadshttps://www.threads.net/@{user}p_threads_32.png#000000N/A — use existing asset
GitHubhttps://github.com/{user}p_github_32.png#333333N/A — use existing asset
Linktreehttps://linktr.ee/{user}p_linktree_32.png#39e09bN/A — use existing asset
ID.MyOpenLink.NEThttps://id.myopenlink.net/~{name}/Public/p_myopenlink_32.png#2563ebN/A — use existing asset
Carrdhttps://{user}.carrd.co/p_carrd_32.png#2eaadcN/A — use existing asset
Glitchhttps://glitch.com/~{user}p_glitch_32.png#65387dN/A — use existing asset
Facebookhttps://facebook.com/{user}p_facebook_32.png#1877f2N/A — use existing asset
Instagramhttps://www.instagram.com/{user}p_insta_32.png#e4405fN/A — use existing asset
TikTokhttps://www.tiktok.com/@{user}p_tiktok_32.png#000000N/A — use existing asset
RSSFeed URL (e.g. podcasts RSS)p_rss_32.png#ff6600N/A — use existing asset
AtomFeed URL (e.g. podcasts Atom)p_atom_32.png#ff6600N/A — use existing asset
Emailmailto:{email}p_email_32.png#666666https://www.flaticon.com/free-icons/email

Icon Provisioning Rules

  1. Prefer existing assets — if p_{key}_32.png already exists in assets/, copy it to the output directory. No download needed.
  2. 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.
  3. Resize to 32×32 — use macOS sips:
    curl -sL -o /tmp/icon.png "{logo-url}" && sips -z 32 32 /tmp/icon.png --out "assets/p_{key}_32.png"
    For PNG downloads at larger sizes, sips -z 32 32 handles downscaling.
  4. Fall back to p_none.png — if no official logo can be found, use the generic placeholder.
  5. Copy to output directory — after provisioning/resizing, copy p_{key}_32.png from assets/ to the credential output directory alongside index.html.

Social Cross-Reference Pattern

When adding social platform URLs to a credential set, ALL four profile files must be updated consistently:

FileWhat to add
profile.ttlowl: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 icon

Download now (ZIP)

Does 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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.