Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 62 additions & 20 deletions tools/render-og-card.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
"""Render /og.png, the 1200x630 social card for agentrust-io.com, with headless Chrome.
"""Render AgenTrust social cards (1200x630 PNG) with headless Chrome.

python tools/render-og-card.py # writes og.png
python tools/render-og-card.py # agentrust-io.com, writes og.png
python tools/render-og-card.py wcm --out PATH # wcm.agentrust-io.com card
CHROME=/path/to/chrome python tools/render-og-card.py

The card is HTML so its words can change with an edit, not a lost design file.
It lives here rather than as a page because every *.html in the repository is a
published page to check-site.py and the sitemap. Keep the words in step with the
homepage hero and with the og:image:alt text the pages use; colours are the brand
navy and red from brand/BRAND.md.
Every card is the same HTML template with different words, so the family stays
consistent and the next wording change is an edit and one command, not a lost
design file. Cards for the docs hosts are committed in their own repositories
(the WCM card lives at docs/assets/og.png in weight-custody-manifest), so pass
--out for those.

The template lives here rather than as a page because every *.html in this
repository is a published page to check-site.py and the sitemap. Keep the words
in step with each site's hero and its og:image:alt; colours are the brand navy
and red from brand/BRAND.md.

No --check: a PNG rendered on another machine differs in font hinting, so commit
the rendered file and review it by eye.
"""
import argparse
import os
from pathlib import Path
import shutil
Expand All @@ -20,12 +27,26 @@
import tempfile

ROOT = Path(__file__).resolve().parents[1]

EYEBROW = 'Open specifications for verifiable AI'
TITLE = 'AgenTrust'
LEAD = 'Prove what your AI ran, and what it did.'
CHAIN = ['Weights', 'Agent', 'Actions', 'Evidence']
DOMAIN = 'agentrust-io.com'

CARDS = {
'hub': {
'out': ROOT / 'og.png',
'eyebrow': 'Open specifications for verifiable AI',
'title': 'AgenTrust',
'lead': 'Prove what your AI ran, and what it did.',
'step': None,
'domain': 'agentrust-io.com',
},
'wcm': {
'out': None,
'eyebrow': 'An AgenTrust open specification',
'title': 'Weight Custody Manifest',
'lead': 'Release model weights only to an attested runtime.',
'step': 1,
'domain': 'wcm.agentrust-io.com',
},
}

CARD = """<!DOCTYPE html>
<html lang="en">
Expand All @@ -36,11 +57,12 @@
body {{ background: #15294B; color: #FFFEFA; position: relative; font-family: Inter, "Segoe UI", ui-sans-serif, sans-serif; }}
.wrap {{ position: absolute; left: 90px; right: 90px; top: 84px; }}
.eyebrow {{ font-size: 25px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #9FB3D1; }}
h1 {{ font-size: 118px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }}
h1 {{ font-size: {title_size}px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }}
.bar {{ width: 120px; height: 9px; background: #B91C1C; margin: 38px 0 34px; }}
.lead {{ font-size: 44px; line-height: 1.2; color: #E9E4D8; margin: 0; }}
.chain {{ margin-top: 30px; font-size: 27px; color: #9FB3D1; letter-spacing: .01em; }}
.chain b {{ color: #FFFEFA; font-weight: 600; }}
.chain .on {{ color: #FFFEFA; border-bottom: 3px solid #B91C1C; padding-bottom: 2px; }}
.domain {{ position: absolute; left: 90px; bottom: 46px; font-size: 27px; font-weight: 700; color: #9FB3D1; }}
.foot {{ position: absolute; left: 0; right: 0; bottom: 0; height: 14px; background: #B91C1C; }}
</style>
Expand Down Expand Up @@ -73,17 +95,37 @@ def chrome():
raise SystemExit('Chrome not found. Set CHROME to its path.')


def chain_html(step):
parts = []
for i, name in enumerate(CHAIN, 1):
item = f'<b>{i:02d}</b> {name}'
parts.append(f'<span class="on">{item}</span>' if i == step else item)
return ' &nbsp;·&nbsp; '.join(parts)


def main():
chain = ' &nbsp;·&nbsp; '.join(f'<b>{i:02d}</b> {step}' for i, step in enumerate(CHAIN, 1))
html = CARD.format(eyebrow=EYEBROW, title=TITLE, lead=LEAD, chain=chain, domain=DOMAIN)
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument('card', nargs='?', default='hub', choices=sorted(CARDS))
parser.add_argument('--out', type=Path, help='where to write the PNG (required for docs-host cards)')
args = parser.parse_args()

card = CARDS[args.card]
out = args.out or card['out']
if out is None:
raise SystemExit(f'the {args.card} card is committed in its own repository: pass --out')
html = CARD.format(
eyebrow=card['eyebrow'], title=card['title'], lead=card['lead'], domain=card['domain'],
chain=chain_html(card['step']),
# One line at 1020px wide: long names step down rather than wrap.
title_size=118 if len(card['title']) <= 12 else 84,
)
with tempfile.TemporaryDirectory() as tmp:
card = Path(tmp) / 'og-card.html'
card.write_text(html, encoding='utf-8')
out = ROOT / 'og.png'
page = Path(tmp) / 'og-card.html'
page.write_text(html, encoding='utf-8')
subprocess.run([chrome(), '--headless=new', '--disable-gpu', '--hide-scrollbars',
'--force-device-scale-factor=1', '--window-size=1200,630',
f'--screenshot={out}', card.as_uri()], check=True, capture_output=True)
print(f'wrote {out.relative_to(ROOT)}')
f'--screenshot={Path(out).resolve()}', page.as_uri()], check=True, capture_output=True)
print(f'wrote {out}')


if __name__ == '__main__':
Expand Down
17 changes: 9 additions & 8 deletions wcm/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
address is the drift. The Open Graph tags below point at the docs site so a
crawler that lands on an old share resolves to the same card.

og-launch.png stays in this directory. Cards cached against
agentrust-io.com/wcm/og-launch.png still resolve, and nothing rewrites that path.
The card is served from this directory as og.png, the same image the docs
site serves, rendered by tools/render-og-card.py wcm. og-launch.png, the old
card, stays here too, so shares cached against it still resolve.
-->

<link rel="canonical" href="https://wcm.agentrust-io.com/">
Expand All @@ -32,22 +33,22 @@
<meta property="og:description" content="An open, pre-1.0 specification for releasing model weights only to an approved, attested runtime, with the guarantee boundary stated plainly.">
<meta property="og:url" content="https://wcm.agentrust-io.com/">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://agentrust-io.com/wcm/og-launch.png">
<meta property="og:image:width" content="1731">
<meta property="og:image:height" content="909">
<meta property="og:image:alt" content="Weight Custody Manifest: Release, Attest, Revoke">
<meta property="og:image" content="https://agentrust-io.com/wcm/og.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Weight Custody Manifest: release model weights only to an attested runtime">

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Weight Custody Manifest">
<meta name="twitter:description" content="An open, pre-1.0 specification for releasing model weights only to an approved, attested runtime, with the guarantee boundary stated plainly.">
<meta name="twitter:image" content="https://agentrust-io.com/wcm/og-launch.png">
<meta name="twitter:image" content="https://agentrust-io.com/wcm/og.png">

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">

<link rel="stylesheet" href="/design-system.css?v=18">
<link rel="stylesheet" href="/design-system.css?v=21">
<script>
// replace() rather than assign(): Back should return to wherever the reader
// came from, not bounce them through this page again.
Expand Down
Binary file added wcm/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions wcm/redirect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,20 @@ assert.ok(
'og:url must be the docs home',
);

// The card image stays served from this directory: links cached against
// agentrust-io.com/wcm/og-launch.png must keep resolving.
// The card is served from this directory, so a share of this address never
// depends on another host. og.png is the current card; og-launch.png, the old
// one, stays because links cached against it must keep resolving.
assert.ok(
fs.existsSync(path.join(here, 'og-launch.png')),
'og-launch.png must stay in place for already-shared cards',
);
assert.ok(
html.includes('content="https://agentrust-io.com/wcm/og-launch.png"'),
'og:image must be absolute and point at the image this directory still serves',
fs.existsSync(path.join(here, 'og.png')),
'og.png, the current card, must be served from this directory',
);
assert.ok(
html.includes('<meta property="og:image" content="https://agentrust-io.com/wcm/og.png">'),
'og:image must be absolute and point at the card this directory serves',
);

// A redirect stub is small. If this trips, someone is rebuilding a second
Expand Down
Loading