Add statements, not a second document.
Any element with rdf-predicate is a statement
carrier. That element locally determines a subject, predicate,
object, and optional graph name. Nesting is useful HTML, but it
never supplies hidden RDF state.
- Subject
- The element itself by default, or an explicit IRI or local blank-node key.
- Predicate
- A full IRI reference in
rdf-predicate. - Object
- Visible text, a native HTML value, a native IRI, a local key, or a direct child template.
- Graph
- The default graph unless
rdf-graphorrdf-graph-keyis present.
Use full IRI references. Core 0.1 has no prefix
declarations or compact-IRI expansion. Write
https://schema.org/name, not
schema:name, in an HTML attribute.
Publish two facts in ordinary HTML.
Declare the binding on html, give the document a
stable canonical location, and annotate the elements already
carrying the information.
<!doctype html>
<html lang="en" rdf-version="1.2">
<head>
<link rel="canonical" href="https://example.com/notes">
</head>
<body>
<article id="note-7">
<h1
rdf-subject="#note-7"
rdf-predicate="https://schema.org/headline"
>A field note</h1>
<time
datetime="2026-07-15"
rdf-subject="#note-7"
rdf-predicate="https://schema.org/dateCreated"
rdf-datatype="http://www.w3.org/2001/XMLSchema#date"
>July 15, 2026</time>
</article>
</body>
</html>
<https://example.com/notes#note-7>
<https://schema.org/headline> "A field note" ;
<https://schema.org/dateCreated>
"2026-07-15"^^<http://www.w3.org/2001/XMLSchema#date> .
The article is addressable because it has an
id. It does not assert anything by itself. Each child
statement explicitly names #note-7, so moving either
child elsewhere in the same document cannot silently change what
it describes.
Let the carrier describe itself, until it should not.
Subject construction follows one fixed order. An explicit
rdf-subject wins, then
rdf-subject-key, then the carrier’s own non-empty
id. With none of those, the carrier gets a fresh blank
node for that extraction.
<h1
id="alice"
rdf-predicate="https://schema.org/name"
>Alice</h1>
<#alice> <https://schema.org/name> "Alice" .<article id="alice">
<h1
rdf-subject="#alice"
rdf-predicate="https://schema.org/name"
>Alice</h1>
</article>
<#alice> <https://schema.org/name> "Alice" .
There is no subject inheritance. A parent
id makes the parent referable; it does not become
the child’s RDF subject unless the child says so.
Use the HTML value that already means the thing.
IA² does not add a general rdf-object mini-language.
The object comes from one native carrier on the statement
element.
| Object kind | Authoring form | Typical use |
|---|---|---|
| Visible literal | Element text | Names, descriptions, labels |
| Exact literal | meta[content], data[value], time[datetime] | Dates, codes, machine-exact values |
| IRI | Native href, src, cite, or other allowed single-IRI carrier | Links to people, artifacts, sources |
| Reusable blank node | rdf-object-key | Structured anonymous resources |
| RDF 1.2 triple term | One direct child template | Claims about an unasserted proposition |
IRI objects stay native links
<a
href="https://orcid.org/0000-0002-1825-0097"
rdf-subject="#alice"
rdf-predicate="https://schema.org/sameAs"
>Alice’s ORCID</a>
<#alice> <https://schema.org/sameAs>
<https://orcid.org/0000-0002-1825-0097> .Keys correlate anonymous structure
<span
rdf-subject="#ticket-42"
rdf-predicate="https://schema.org/location"
rdf-object-key="location"
></span>
<span
rdf-subject-key="location"
rdf-predicate="https://schema.org/name"
>Lab 3</span>
<#ticket-42> <https://schema.org/location> _:location .
_:location <https://schema.org/name> "Lab 3" .The source key is only an extraction-local correlation handle. A serializer chooses its own blank-node label.
Put datatype, language, and direction where they apply.
rdf-datatype gives a literal datatype. Native
lang and dir give language and base
direction, but only when directly present on the statement
carrier.
<time
datetime="2026-07-15"
rdf-subject="#release"
rdf-predicate="https://schema.org/datePublished"
rdf-datatype="http://www.w3.org/2001/XMLSchema#date"
>15 July 2026</time>
<p
lang="fr"
rdf-subject="#release"
rdf-predicate="https://schema.org/name"
>Architecture de l’information</p>
<#release> <https://schema.org/datePublished>
"2026-07-15"^^<http://www.w3.org/2001/XMLSchema#date> ;
<https://schema.org/name> "Architecture de l’information"@fr .
Literal metadata never inherits for RDF. An
ancestor’s lang still affects HTML presentation and
accessibility, but it does not silently change an extracted
literal. Datatype and language are mutually exclusive.
Name graphs locally. Quote propositions without asserting them.
Named graphs
Put rdf-graph or rdf-graph-key directly
on every statement that belongs to that graph. A non-statement
element carrying the attribute declares an empty named graph.
<data
value="approved"
rdf-subject="#decision-9"
rdf-predicate="https://schema.org/status"
rdf-graph="#audit"
></data>
<span hidden rdf-graph="#empty-review-queue"></span>
RDF 1.2 triple terms
A direct child template can carry one unasserted
statement as the outer statement’s object. Template contents live
in a separate DocumentFragment, so the inner
proposition is not accidentally asserted by a document-wide DOM
query.
<div
rdf-subject="#review"
rdf-predicate="https://example.com/quotes"
>
<template>
<a
href="https://example.com/bob"
rdf-subject="https://example.com/alice"
rdf-predicate="https://schema.org/knows"
>Bob</a>
</template>
</div>
<#review> <https://example.com/quotes>
<< <https://example.com/alice>
<https://schema.org/knows>
<https://example.com/bob> >> .Extract the document you actually mean.
A downloaded HTML document is an inert observation. An
application-mutated DOM is a runtime observation. IA² permits
both. Each extraction is a snapshot of the supplied
Document or DocumentFragment; extraction
itself executes nothing.
Add the document navigator
The browser bundle mounts one navigator, extracts the current DOM, and refreshes after semantic mutations. This guide includes it, so its own RDF is available from the launcher on this page.
<script
type="module"
src="https://ia2.dev/packages/html-rdf-navigator/dist/html-rdf-navigator.js"
></script>
- Inert acquisition: parse downloaded HTML and extract without running its scripts.
- Live acquisition: extract the current light DOM after the host application changes it.
- Separate fragments: pass a template’s
contentexplicitly when that inert fragment is the intended root.
Describe the document as a graph, then render it.
RDF/HTML is the complementary direction to HTML/RDF. Turtle or TriG describes parsed document nodes, element classes, attributes, text, and enough child-order constraints to derive a sibling sequence. A renderer derives faithful ordinary HTML and can also publish RDF statements not used structurally inside an augmented HTML/RDF result.
Why describe HTML in RDF?
HTML is an excellent finished document format, but its tree is often the end of a pipeline. RDF/HTML is useful when the document should remain a view of a larger graph. Giving document nodes RDF identity lets the same dataset describe what a node renders, what domain resource it concerns, where it came from, and how it relates to evidence or decisions outside the page.
- Graph-native views: derive a human-readable document from RDF without reducing the source graph to template variables or an opaque HTML string.
- Independent composition: let separate graphs or tools contribute named nodes, annotations, and ordering constraints before one document is materialized.
- Meaning survives rendering: consume the structural statements needed to build the DOM, then carry the remaining domain and provenance statements into the result as HTML/RDF.
It is not a replacement for ordinary HTML authoring. Use HTML directly when the page is the primary artifact and no graph-native composition, node identity, or preserved RDF context is needed.
- HTML/RDF
- Start with an HTML host and extract its RDF 1.2 dataset.
- RDF/HTML
- Start with an RDF 1.2 dataset and derive one or more HTML documents.
- Fidelity
- The graph describes the parsed DOM, including explicit text and comment nodes, not the original source bytes.
- Safety
- The public renderer and extension use inert previews. Description never grants execution.
Declare the vocabulary snapshot
Every renderable document identifies the dated vocabulary it was authored against. The renderer recognizes supported snapshots locally; it does not fetch the vocabulary during rendering.
@prefix dcterms: <http://purl.org/dc/terms/> .
doc:page
a rdfhtml:Document ;
rdfhtml:base <https://example.com/page/> ;
dcterms:conformsTo <https://ia2.dev/spec/rdf-html/vocabulary/rdf-html-2026-07-18.ttl> .
A dataset may contain partial RDF/HTML descriptions assembled by several sources. Materialization begins only when the selected document has one complete, unambiguous tree. The renderer reports incomplete membership or ordering instead of inventing missing structure.
Write known attributes directly
Each indexed HTML attribute has a generated RDF property. Use that property directly and preserve the exact DOM value as a string. Present boolean attributes use the empty string.
@prefix rdfhtml: <https://ia2.dev/spec/rdf-html#> .
doc:save
a rdfhtml:Button ;
rdfhtml:id "save-draft" ;
rdfhtml:accessKey "S" ;
rdfhtml:disabled "" .
Use an expanded rdfhtml:Attribute occurrence for an
unknown or extension attribute, or when the occurrence itself
needs identity, provenance, or annotations. Known attributes can
also use the expanded form with
rdfhtml:attributeDefinition, but the direct property
is the ordinary authoring form.
doc:save rdfhtml:attribute [
a rdfhtml:Attribute ;
rdfhtml:attributeName "data-review-state" ;
rdfhtml:attributeValue "pending"
] .
Use a collection for a complete child sequence
rdfhtml:children is the compact default when one
description owns a parent's complete sequence. Its RDF Collection
states membership and immediate order together. Anonymous
[] nodes remain convenient for children that need no
identity outside that description, and converters emit this form.
doc:body rdfhtml:children (
[ a rdfhtml:H1 ; rdfhtml:children (
[ a rdfhtml:Text ; rdfhtml:data "Welcome" ]
) ]
[ a rdfhtml:P ; rdfhtml:children (
[ a rdfhtml:Text ; rdfhtml:data "Hello world!" ]
) ]
) .
Use flat relations for independent contributions
A collection is intentionally complete and changing its middle
means changing the shared RDF list spine. Use named children,
rdfhtml:hasChild, and ord: relations when
different graphs or tools contribute nodes independently, when a
child is annotated elsewhere, or when only relative order is
meaningful. One rdfhtml:hasChild assertion anchors a
connected ordering chain; normalization derives the other child
memberships. The vocabulary supplies the ordering types, so
authors do not repeat them.
@prefix ord: <https://ontology.inferal.com/modules/ordering/> .
doc:body rdfhtml:hasChild doc:heading .
doc:heading
a rdfhtml:H1 ;
ord:precedes doc:status .
doc:status
a rdfhtml:P ;
ord:precedes doc:actions .
doc:actions
a rdfhtml:Nav .
Use ord:immediatelyPrecedes only when adjacency is
part of the assertion. If the important fact is merely that one
child comes before another, ord:precedes, or inverse
ord:follows, is usually more accurate and remains
valid if another contributor inserts a child between them. A
renderable parent must still have enough combined constraints to
yield one sequence; the renderer does not invent a tie-break.
The inverse forms rdfhtml:childOf and
ord:follows are equivalent when that direction reads
more naturally.
Keep mixed content explicit. Do not use an
aggregate textContent value. Represent text as
rdfhtml:Text nodes and order those nodes beside
elements, so Hello <em>world</em>! remains
three distinct children.
Convert an existing HTML document
The package can walk a parsed HTML DOM and emit the same compact RDF/HTML structure. It preserves document type, elements, attributes, text, comments, and template contents. The result describes the parsed DOM, so it does not preserve source whitespace choices or other byte-level formatting.
npx @ia2-dev/rdf-html describe page.html \
--output page.ttl \
--document-iri https://example.com/page#document \
--base https://example.com/page/
The same command accepts an HTTP or HTTPS URL. In the other
direction, npx @ia2-dev/rdf-html render page.ttl
writes the faithful described HTML to standard output. Add
--publication to retain unconsumed RDF in an HTML/RDF
island. Use
--document for one document in a multi-document
Turtle or TriG source, or --workspace for the inert
selector workspace.
Renderability is not complete HTML validation. Generated categories, attribute applicability, and value syntax are inspectable metadata. RDF/HTML validates that one DOM can be derived; a separate reviewed profile or HTML validator checks whether that DOM follows all WHATWG authoring rules.
Open a public Turtle or TriG document
The URL renderer fetches at most 2 MiB from a public HTTP or HTTPS
location, blocks private-network destinations and unsafe
redirects, then renders a single declared document without
visible renderer chrome. When a source describes several
documents, a compact selector remains outside the sandboxed
document frame. The IA² browser extension applies the package's
inert workspace renderer to browser-opened .ttl and
.trig documents.
Carry a graph and its resources in one HTML document.
HARE, the proposed HTML Agent Resource Envelope, packages a manifest, relationships, and inspectable representations in a browser-openable file. Semantic HTML can live directly in the document as template DOM. The manifest is HTML/RDF, not a parallel JSON document, so agents can extract it without running code.
Use HARE when a handoff must preserve meaning and the resources that meaning describes. Keep the graph as the primary model. Give DOM representations logical paths so they receive virtual document URLs; paths remain optional for other representations. Use byte representations only when exact octets, downloads, or hashes matter. The envelope remains useful as HTML even when no viewer runs.
HARE reuses dcterms:hasPart for membership,
dcterms:identifier for logical paths,
and dc:format for media types. It defines only the
envelope mechanics that those vocabularies do not provide,
including the manifest graph, virtual document base,
representation link, carrier, and optional runtime. Byte representations additionally use
cred:digestSRI for digests and HARE's decoded byte
length.
- Envelope
- The self-contained HTML artifact and its source document identity.
- Manifest
- One named RDF graph describing resources, representations, relationships, and policy.
- Resource
- A conceptual item in the handoff, such as a brief, dataset, image, or decision.
- Representation
- A semantic or byte realization of a resource, with a media type and an HTML carrier.
Build the smallest declarative envelope
This complete envelope carries a small HTML resource as parsed, directly inspectable template DOM. The logical path is a useful alias, not the identity or source of truth for the resource.
<!doctype html>
<html lang="en" rdf-version="1.2">
<head>
<meta charset="utf-8">
<link rel="canonical" href="https://example.com/hello.hare.html">
</head>
<body>
<main>
<h1>Hello handoff</h1>
<section id="manifest" aria-label="Bundle manifest">
<a hidden href="https://ia2.dev/spec/resource-envelope#Envelope"
rdf-subject="" rdf-predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
rdf-graph="#manifest"></a>
<a hidden href="https://ia2.dev/spec/resource-envelope#HARE-0.1" rdf-subject=""
rdf-predicate="http://purl.org/dc/terms/conformsTo"
rdf-graph="#manifest"></a>
<a hidden href="https://ia2.dev/spec/resource-envelope#DeclarativeProfile" rdf-subject=""
rdf-predicate="http://purl.org/dc/terms/conformsTo"
rdf-graph="#manifest"></a>
<a hidden href="#manifest" rdf-subject=""
rdf-predicate="https://ia2.dev/spec/resource-envelope#manifestGraph"
rdf-graph="#manifest"></a>
<a hidden href="https://hello.hare.invalid/" rdf-subject=""
rdf-predicate="https://ia2.dev/spec/resource-envelope#virtualBase"
rdf-graph="#manifest"></a>
<a href="#greeting" rdf-subject=""
rdf-predicate="http://purl.org/dc/terms/hasPart"
rdf-graph="#manifest">Greeting resource</a>
<a hidden href="#greeting-representation" rdf-subject="#greeting"
rdf-predicate="https://ia2.dev/spec/resource-envelope#representation"
rdf-graph="#manifest"></a>
<a id="greeting-representation" hidden
href="https://ia2.dev/spec/resource-envelope#DOMRepresentation"
rdf-subject="#greeting-representation"
rdf-predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
rdf-graph="#manifest"></a>
<data value="/hello.html" rdf-subject="#greeting-representation"
rdf-predicate="http://purl.org/dc/terms/identifier"
rdf-graph="#manifest">/hello.html</data>
<data hidden value="text/html" rdf-subject="#greeting-representation"
rdf-predicate="http://purl.org/dc/elements/1.1/format"
rdf-graph="#manifest"></data>
<a hidden href="#greeting-content" rdf-subject="#greeting-representation"
rdf-predicate="https://ia2.dev/spec/resource-envelope#carrier"
rdf-graph="#manifest"></a>
</section>
</main>
<template id="greeting-content">
<article>
<h1>Hello, agent</h1>
<p>This semantic HTML is readable directly in the envelope.</p>
<a href="/">Return to the envelope document</a>
</article>
</template>
</body>
</html>
The declared hare:virtualBase gives the host envelope
the virtual URL https://hello.hare.invalid/. The
greeting's logical path gives it
https://hello.hare.invalid/hello.html. Inside the
template, href="/" returns to the host document, and
ordinary relative paths can address other bundled
representations. These virtual URLs are routing addresses, not
retrievable locations or RDF identity.
When exact bytes are part of the handoff contract, use a
hare:ByteRepresentation instead. Its carrier is an
inert base64 <script type="application/octet-stream">
and its byte length and SRI digest describe the decoded octets.
One resource may have both semantic and byte representations.
Materialize a small site without inventing an origin
Template HTML can use ordinary relative URLs for passive resources. A materializing viewer resolves each reference against the containing document's virtual URL, finds the matching byte representation, verifies it, and rewrites only the derived view to a local Blob URL. The template stays readable and unchanged.
<template id="report-content">
<link rel="stylesheet" href="../styles/report.css">
<article>
<img src="../images/verified.svg" alt="Verified">
<a href="details.html">Details</a>
</article>
</template>
Navigation and materialization deliberately differ. The anchor
routes to another virtual document when activated; the image and
stylesheet are passive fetch destinations and may be
materialized immediately. Stylesheet imports and CSS
url() references can recurse through the same
verified process. External, missing, incompatible, and
executable destinations stay inert; a viewer does not use the
live network as fallback.
Verified Markdown bytes can use the same virtual address space.
For example, a brief at /docs/brief.md may contain
. A safe
renderer resolves that reference to
/assets/verified.svg, verifies the declared image
representation, and gives only the rendered image a temporary
Blob URL. The Markdown bytes and reference remain unchanged;
unmatched images are never fetched from the network.
The optional @ia2-dev/hare-viewer runtime includes a
small dependency-free Markdown renderer for headings, emphasis,
links, images, quotations, lists, task items, tables, and code.
It creates DOM nodes directly, treats raw HTML as text, routes
links through HARE, and delegates images to verified resource
lookup. It is intentionally not advertised as CommonMark
conformant until it is tested against that specification's
corpus. Self-viewing envelopes can inline the renderer with the
rest of their runtime.
A safe viewer also makes links inert in its derived iframe before
showing it, retains the original href as routing
data, and resolves that value through the HARE virtual URL space
when the user activates the link. This prevents a timing gap from
turning a virtual .invalid address into a browser
navigation. The readable links in the authored template are not
changed.
The authored host document can refer to a bundled passive
resource too. Keep its virtual URL in an inert
data-hare-src attribute so the browser does not try
the non-retrievable address before verification. A supporting
viewer retains that declarative reference and supplies the
verified Blob URL to src at runtime.
<figure>
<img
data-hare-src="https://hello.hare.invalid/images/verified.svg"
alt="Verified bundle"
>
<figcaption>Image carried by this envelope.</figcaption>
</figure>
Keep the host policy compatible. A
srcdoc preview inherits the envelope's Content
Security Policy, and its inner policy can only restrict it
further. If browser viewers are intended to work, permit
about: and blob: frames plus
blob:/data: for the passive style,
image, media, and font destinations the viewer materializes.
Continue to deny scripts, workers, plugins, and network
fallback.
DevTools, test runners, and other extensions sometimes inject
inspection code into the preview. The browser may report that
attempt as blocked script execution even though the derived
document is script-free. That is the sandbox working. Confirm
the derived document contains no script; do not add
allow-scripts to silence the message.
Enhance it only when a viewer helps
A declarative envelope needs no executable code. During
development, @ia2-dev/hare-viewer can generate a file
browser when the body has no authored interface, or add Document
and Files tabs when authored content is present. A compatible
browser extension can add those modes automatically, including
the full file browser for a bare envelope, without requiring or
executing envelope-supplied code.
<script
type="module"
src="https://ia2.dev/packages/hare-viewer/dist/hare-viewer.js"
></script>
Keep transport self-contained. The package URL is convenient while authoring. Inline the built runtime into an exported self-viewing envelope, and never execute bundled HTML or scripts by default.
Give an agent the model, not a pasted prompt.
The IA² repository packages HTML/RDF and HARE as two portable
Agent Skills. Each skill keeps its working instructions in
SKILL.md, then loads detailed references or the HARE
byte-commitment helper only when the task needs them.
ia2-html-rdf- Author, extract, and review locally complete RDF 1.2 statements in HTML.
ia2-hare- Create and verify HARE manifests, representations, byte commitments, routing, and viewer security.
plugins/ia2- The distributable package, with thin Codex and Claude Code manifests around the same skill folders.
Get the portable source
Clone the repository once. The directories under
plugins/ia2/skills/ are the cross-client source of
truth.
git clone --depth 1 https://github.com/ia2-dev/ia2.git
cd ia2
mkdir -p .agents/skills
cp -R plugins/ia2/skills/ia2-html-rdf .agents/skills/
cp -R plugins/ia2/skills/ia2-hare .agents/skills/
$ia2-html-rdf
$ia2-hare
claude --plugin-dir ./plugins/ia2
/ia2:ia2-html-rdf
/ia2:ia2-hare
Install at the scope you mean
The Codex example installs repository-scoped skills. Copy the
same two directories to ~/.agents/skills/ when they
should be available to Codex across repositories. For persistent
Claude Code project skills, copy them to
.claude/skills/; use
~/.claude/skills/ for personal scope. Skills copied
directly into Claude Code use /ia2-html-rdf and
/ia2-hare without the plugin namespace.
Other Agent Skills compatible clients can install either skill
directory in their supported skills location. The portable
contract is the folder containing SKILL.md, its
references, and optional scripts. The
agents/openai.yaml files add Codex interface metadata
but do not change the instructions.
Deploy the wrapper, maintain the core. The
package includes both .codex-plugin/plugin.json and
.claude-plugin/plugin.json for marketplace or team
distribution. Keep edits in the shared skill folders so every
host receives the same IA² and HARE behavior.
Check the statement at the carrier.
Common mistakes
Expecting nesting to chain subjects. It never does.
Using prefixed names in attributes. Core resolves IRI references; it does not expand prefixes.
Combining object carriers. Do not put a native IRI, exact literal, local object key, and object template on the same statement.
Treating local keys as persistent IDs. They correlate blank nodes only within one extraction.
Assuming extraction validates or executes SHACL. Shapes and rules are extracted as RDF data; validation and execution are separate operations.