OCL spec v0.1 (draft)
An open, machine-readable label saying who a piece of digital content suits (age) and what it does to the viewer (influence), signed by the rater who made it. Anyone may rate, attach, read and check labels under this spec, free of charge.
Draft of 31 October 2026, published by Shinifrontiers Technologies Private Limited as a proposal (including to the EU KIDS Act process). Field names may change before v1.0. Plain-language explainer: the OCL fingerprint.
1. Roles and principles
- Rater: rates an item and signs its label. Identified by an internet host name (for example
rate.shinifrontiers.com) that publishes its public keys. - Creator: attaches the signed label to the item.
- Checker: anyone (browser, platform, parent app, regulator) who reads a label and checks it.
- A valid signature proves who rated an item, not that the rating is right. Which raters count is for the regulator to list, not for any rater.
- Ratings are advisory opinions. A label is never a certification, a guarantee or legal advice.
- Labels hold no personal data. Checking a label needs no tracking script and sends nothing about the viewer to the rater.
- Labelling never changes the item: no re-encoding, watermarking or editing.
2. The label
A label is a JSON object (UTF-8). All fields below are required in v0.1.
| Field | Type | Meaning |
|---|---|---|
label_id | string | Rater's ID for this label (letters and digits, up to 64). |
spec_version | string | "0.1" |
item_type | string | "website" in v0.1. Reserved: web_page, app, video, chatbot. |
subject | string | What was rated. For a website: its host name, lower case (www. is ignored when checking). |
rater | string | Host name of the rater that signed the label. New in v0.1. |
age_band | string | One of all_ages, mild, teen, older_teen, adult: the youngest age for whom the content is not unsuitable, judged from evidence of harm, never from who the content is written for. |
age_band_scales | object | The band on local scales (table below). |
influence | object | All 12 categories, each an integer 0–3 (0 none, 1 some, 2 clear, 3 strong). |
flags | array | Zero or more of undisclosed_ad, unverified_claim, imitation_risk, body_pressure, hate_or_targeting. |
confidence | string | high, medium or low. |
method | string | The rater's method and version, e.g. ocl-website-0.1. |
rated_at | string | Date and time of rating, ISO 8601 with offset. |
expires_on | string | Last valid date, YYYY-MM-DD. At most 90 days after rating for websites, because websites change. |
signature | string | Detached signature (section 3). |
Age band on local scales
| age_band | India | EU KIDS Act (proposal) | PEGI |
|---|---|---|---|
all_ages | U | 3-12 (child apps only) | 3 |
mild | 7+ | 3-12 (child apps only) | 7 |
teen | 13+ | 13-14 | 12 |
older_teen | 16+ | 15-17 | 16 |
adult | A | 18+ | 18 |
Influence categories
fun, education, inspiration (what the content gives) and fight, threat_fear, sexual, substances, risky_stunts, commercial_push, political, body_pressure, hate (what it pushes).
Example
{
"label_id": "754fe7cfd476531e0b95fb6c",
"spec_version": "0.1",
"item_type": "website",
"subject": "example.com",
"rater": "rate.shinifrontiers.com",
"age_band": "teen",
"age_band_scales": {"india": "13+", "eu_kids_act": "13-14", "pegi": "12"},
"influence": {"fun": 2, "education": 1, "inspiration": 0, "fight": 0, "threat_fear": 0,
"sexual": 0, "substances": 0, "risky_stunts": 1, "commercial_push": 1,
"political": 0, "body_pressure": 0, "hate": 0},
"flags": ["imitation_risk"],
"confidence": "medium",
"method": "ocl-website-0.1",
"rated_at": "2026-10-31T09:00:00+00:00",
"expires_on": "2027-01-29",
"signature": "eyJhbGciOiJFZERTQSIsImtpZCI6Im9jbC1yYXRlci0xIn0..<signature>"
}
3. Signing
- Canonical form: the label without its
signaturefield, serialised as JSON with keys sorted, no spaces (separators,and:), UTF-8, non-ASCII characters left as they are. - Header:
{"alg":"EdDSA","kid":"<key id>"}. - Signing input:
base64url(header) + "." + base64url(canonical form)(base64url without padding). - Signature: Ed25519 (pure EdDSA, RFC 8032) over the signing input.
- Detached form in the label:
"signature": "<header>..<signature>"(empty middle part), so the label stays readable JSON. - Compact token for pages:
<header>.<canonical payload>.<signature>, a standard JWS that carries the whole label.
Rater keys
Each rater publishes its public keys as a JWK set at https://<rater>/.well-known/ocl-keys.json, readable from any origin:
{"keys": [{"kty": "OKP", "crv": "Ed25519", "kid": "ocl-rater-1", "x": "<public key, base64url>", "use": "sig"}]}
Only public parts are published. A rater keeps a retired key in this file until every label signed with it has expired. Our keys: /.well-known/ocl-keys.json; our signing key is held in a hardware-backed key service and cannot be exported.
4. Attaching a label to a website
A website publishes either or both of these. The file is preferred.
File: /.well-known/ocl.json
{
"ocl": "0.1",
"site": "https://example.com",
"labels": [{
"paths": ["/*"],
"label": "https://rate.shinifrontiers.com/label/754fe7cfd476531e0b95fb6c",
"rater": "rate.shinifrontiers.com",
"signed_label": { ...the signed label... }
}]
}
Tags in the <head> of each page
<link rel="ocl-label" href="https://rate.shinifrontiers.com/label/754fe7cfd476531e0b95fb6c"> <meta name="ocl" content="<compact token>">
In v0.1 one label covers the whole site (paths: ["/*"]). Per-path labels, apps (manifest entry and store listing), video (C2PA assertion, metadata added by stream copy) and chatbots (OCL-Label response header) are planned for later versions and described in the explainer; they are not final in v0.1.
5. Checking a label
- Find the label:
https://<host>/.well-known/ocl.json, else the start page's<meta name="ocl">with its<link rel="ocl-label">. Neither present: the site is unrated. - Rater: the entry's
rater, or the host of theocl-labellink. - Keys: fetch
https://<rater>/.well-known/ocl-keys.jsonover HTTPS and pick the key whosekidmatches the signature header. - Signature: verify as in section 3. The label's own
raterfield must equal that rater (www.ignored). - Binding: the label's
subjectmust equal the site checked (www.ignored), so a label copied to another site fails. - Expiry: today must be on or before
expires_on.
The label is valid only if all checks pass. Whether to trust the rater is then the checker's (or regulator's) decision. Proposed default for platforms: content with no valid label is treated as unrated and hidden from child accounts.
Reference checker: our label check page, and its open API GET https://rate.shinifrontiers.com/verify?site=example.com (JSON). It checks labels from any rater.
6. Changes
- v0.1 (31 Oct 2026): first public draft. Adds the required
raterfield inside the signed label, so a label names who signed it.
Comments and proposals: contact@shinifrontiers.com. This specification may be implemented by anyone without charge.