Skip to main content

Implement AiHint on Your Domain

This guide walks you through creating, signing, and deploying an AiHint file. It uses standard command-line tools (OpenSSL, jq, curl) and is language-agnostic.

Prerequisites

You will need:

  • OpenSSL — for key generation and signing
  • jq — for JSON manipulation (install jq)
  • Access to your web server's /.well-known/ directory

Step 1: Generate RSA Keys

Generate a 4096-bit RSA key pair:

# Generate private key
openssl genrsa -out private_key.pem 4096

# Extract public key
openssl rsa -in private_key.pem -pubout -out public_key.pem
Keep your private key secure

The private key is used to sign your AiHint file. Never share it, commit it to version control, or expose it publicly. Only the public key should be published.

Step 2: Publish Your Public Key

Upload public_key.pem to a publicly accessible HTTPS URL on your domain. For example:

https://example.com/.well-known/aihint-pubkey.pem

This URL will be referenced in your AiHint file so that verifiers can fetch the key.

Step 3: Create the AiHint JSON Document

Create a file called aihint.json with the following structure:

{
"version": "0.1",
"type": "global",
"target": "https://example.com",
"issuer": "https://example.com",
"score": 0.75,
"method": "self-assessment-v1",
"issued_at": "2025-06-15T12:00:00Z",
"expires_at": "2026-06-15T12:00:00Z",
"comment": "Self-signed AiHint for example.com",
"public_key_url": "https://example.com/.well-known/aihint-pubkey.pem"
}

Replace the values with your own:

FieldWhat to set
targetYour domain URL
issuerYour domain URL (for self-signed) or the issuer's URL
scoreYour trust score (0.0–1.0)
methodA string identifying your scoring method
issued_atCurrent UTC timestamp in ISO 8601
expires_atExpiration timestamp (e.g., 1 year from now)
public_key_urlURL where your public key is hosted
Generating timestamps
# Current time in ISO 8601 UTC
date -u +"%Y-%m-%dT%H:%M:%SZ"

# One year from now (macOS)
date -u -v+1y +"%Y-%m-%dT%H:%M:%SZ"

# One year from now (Linux)
date -u -d "+1 year" +"%Y-%m-%dT%H:%M:%SZ"

Step 4: Sign the Document

Sign the document using RSA-SHA256:

# Create the canonical payload (everything except signature)
jq 'del(.signature)' aihint.json > payload.json

# Sign the payload
openssl dgst -sha256 -sign private_key.pem -out signature.bin payload.json

# Encode signature as Base64
base64 -i signature.bin -o signature.b64

# Insert the signature into the document
jq --arg sig "$(cat signature.b64 | tr -d '\n')" '. + {signature: $sig}' aihint.json > aihint-signed.json

# Replace the original file
mv aihint-signed.json aihint.json

Step 5: Validate Your File

Before deploying, verify that your file is correctly structured and the signature is valid:

# Check that all required fields are present
jq 'keys' aihint.json

# Verify the signature
jq 'del(.signature)' aihint.json > payload.json
jq -r '.signature' aihint.json | base64 -d > signature.bin
openssl dgst -sha256 -verify public_key.pem -signature signature.bin payload.json
# Should output: "Verified OK"

Step 6: Deploy

Place the signed aihint.json file at your domain's well-known location:

https://example.com/.well-known/aihint.json

Deployment Examples

Apache — place the file in your document root:

cp aihint.json /var/www/html/.well-known/aihint.json

Nginx — ensure the .well-known directory is served:

location /.well-known/ {
root /var/www/html;
default_type application/json;
}

Static hosting (Netlify, Vercel, etc.) — place the file in your project's public/.well-known/ or static/.well-known/ directory.

Verify Deployment

curl -s https://example.com/.well-known/aihint.json | jq .

You should see your signed AiHint document returned.

Step 7: Key Rotation (Ongoing)

Periodically rotate your signing keys:

  1. Generate a new key pair
  2. Publish the new public key at a new URL (or replace the existing one)
  3. Re-sign your AiHint file with the new private key, updating public_key_url
  4. Keep the old public key available until all hints signed with it have expired

Complete Script

Here is a complete script that generates keys, creates, signs, and validates an AiHint file:

#!/usr/bin/env bash
set -euo pipefail

DOMAIN="example.com"
SCORE="0.75"
METHOD="self-assessment-v1"
COMMENT="Self-signed AiHint"
PUBKEY_URL="https://${DOMAIN}/.well-known/aihint-pubkey.pem"

ISSUED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# macOS: date -u -v+1y; Linux: date -u -d "+1 year"
EXPIRES_AT=$(date -u -v+1y +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -d "+1 year" +"%Y-%m-%dT%H:%M:%SZ")

# Generate keys if they don't exist
if [ ! -f private_key.pem ]; then
openssl genrsa -out private_key.pem 4096
openssl rsa -in private_key.pem -pubout -out public_key.pem
fi

# Create the hint document
jq -n \
--arg target "https://${DOMAIN}" \
--arg issuer "https://${DOMAIN}" \
--arg score "$SCORE" \
--arg method "$METHOD" \
--arg issued "$ISSUED_AT" \
--arg expires "$EXPIRES_AT" \
--arg comment "$COMMENT" \
--arg pubkey "$PUBKEY_URL" \
'{
version: "0.1",
type: "global",
target: $target,
issuer: $issuer,
score: ($score | tonumber),
method: $method,
issued_at: $issued,
expires_at: $expires,
comment: $comment,
public_key_url: $pubkey
}' > aihint.json

# Sign it
openssl dgst -sha256 -sign private_key.pem -out sig.bin aihint.json
SIG=$(base64 -i sig.bin | tr -d '\n')
jq --arg sig "$SIG" '. + {signature: $sig}' aihint.json > aihint-signed.json
mv aihint-signed.json aihint.json
rm -f sig.bin

# Verify
jq 'del(.signature)' aihint.json > payload.json
jq -r '.signature' aihint.json | base64 -d > sig.bin
openssl dgst -sha256 -verify public_key.pem -signature sig.bin payload.json
rm -f payload.json sig.bin

echo "AiHint file created and verified: aihint.json"

Next Steps