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
34 changes: 5 additions & 29 deletions .github/workflows/deploy-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ on:
paths:
- 'demo/**'
- 'demo-agents/**'
- 'docs/assets/agent-follows-feedback.gif'
- 'docs/assets/**'
- 'docs/index.html'
- 'scripts/assemble-site.sh'
- 'sop-arena/**'
- '.github/workflows/deploy-demo.yml'
workflow_dispatch: # Allows manual trigger from the GitHub Actions UI
Expand Down Expand Up @@ -107,34 +108,9 @@ jobs:
shell: bash
run: |
set -euo pipefail
rm -rf _site
mkdir -p _site/arena _site/agents
# Technical demo (WASM) lives at the site root: joltrinhq.com/
cp demo/index.html demo/sop.wasm demo/wasm_exec.js demo/404.html demo/favicon.svg demo/favicon.ico demo/og-image.png demo/logo-mark.png _site/
# The documentation index lives at joltrinhq.com/docs/
mkdir -p _site/docs
cp docs/index.html _site/docs/
# The homepage shows this replay from ./assets/
mkdir -p _site/assets
cp docs/assets/agent-follows-feedback.gif _site/assets/
# SOP Arena lives at a subpath of the same site: joltrinhq.com/arena/
cp -r sop-arena/dist/. _site/arena/
# Agent verification barrier (verify in WASM) lives at joltrinhq.com/agents/
cp demo-agents/index.html demo-agents/sop-agents.wasm demo-agents/wasm_exec.js demo-agents/favicon.svg demo-agents/favicon.ico demo-agents/og-image.png demo-agents/logo-mark.png _site/agents/
# Preserve custom domain (e.g. joltrinhq.com) if CNAME exists
if [ -f "CNAME" ]; then
cp CNAME _site/CNAME
elif [ -f "demo/CNAME" ]; then
cp demo/CNAME _site/CNAME
fi
# Fail the deploy if the homepage points at a local file that is not in the site.
for ref in $(grep -oE '(src|href)="\./[^"#?]+"' _site/index.html | sed -E 's/^(src|href)="\.\///; s/"$//'); do
if [ ! -e "_site/${ref}" ]; then
echo "Error: _site/index.html references ./${ref} but it is not in the site" >&2
exit 1
fi
done
ls -lh _site _site/arena _site/agents
# The same script the pull request check runs, so what a PR proves is what ships.
scripts/assemble-site.sh _site
ls -lh _site _site/arena _site/agents _site/assets

- name: Setup GitHub Pages
uses: actions/configure-pages@v5
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/site-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Site Check

# Builds the site the way the Pages deploy does and fails if any file a page
# names (video, poster, image, script) is missing from it. The deploy once
# published a homepage whose video files were never copied, and the local
# tests did not notice because they assemble the site a different way.
on:
pull_request:
paths:
- 'demo/**'
- 'demo-agents/**'
- 'docs/**'
- 'sop-arena/**'
- 'scripts/assemble-site.sh'
- '.github/workflows/deploy-demo.yml'
- '.github/workflows/site-check.yml'

permissions:
contents: read

jobs:
site-assembles:
name: Site assembles with every referenced file
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v7
with:
go-version-file: 'go.mod'
cache: true

- name: Build the WebAssembly demos
shell: bash
run: |
set -euo pipefail
GOROOT="$(go env GOROOT)"
src="${GOROOT}/lib/wasm/wasm_exec.js"
[ -f "$src" ] || src="${GOROOT}/misc/wasm/wasm_exec.js"
cp "$src" demo/wasm_exec.js
cp "$src" demo-agents/wasm_exec.js
(cd demo && GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o sop.wasm .)
(cd demo-agents && GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o sop-agents.wasm .)

- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: 'sop-arena/package-lock.json'

- name: Build SOP Arena
working-directory: ./sop-arena
run: |
npm ci || npm install
npm run build

- name: Assemble the site and check every referenced file
run: scripts/assemble-site.sh _site
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Joltrin (formerly SOP) is an open-source Go library that checks an AI agent's ac
**What Joltrin does.** The agent emits intent. Joltrin checks the intent against the trace and the runbook's rules, outside the agent's context window. The check and the commit are one call, so a step is recorded only if it passed. A step whose required state is missing is refused. A claim that something happened is not evidence that it happened.

<p align="center">
<a href="docs/assets/joltrin-barrier.mp4">
<img src="docs/assets/joltrin-barrier-poster.jpg" alt="A 43 second silent demo: an agent proposes dropping a production database, Joltrin blocks it, the agent runs the missing steps, and the same action is then allowed. Click to play." width="420" />
<a href="https://youtu.be/F0jYkBHJluI">
<img src="docs/assets/joltrin-demo.gif" alt="Joltrin demo. Click to watch the video." width="760" />
</a>
</p>

Expand Down
83 changes: 28 additions & 55 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -477,29 +477,29 @@ <h1 class="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-white tracking-t
</div>
</div>

<figure id="demo-video" class="min-w-0 scroll-mt-20 w-full max-w-[34rem] mx-auto lg:mx-0 lg:ml-auto">
<div class="relative rounded-2xl overflow-hidden border border-dark-700 bg-dark-950 shadow-2xl shadow-black/50" style="aspect-ratio:1/1">
<video id="demo-video-el" class="absolute inset-0 w-full h-full" width="1080" height="1080" muted loop playsinline preload="none"
poster="./assets/joltrin-barrier-poster-540.jpg"
aria-label="A 43 second demo: an agent proposes dropping a production database, Joltrin blocks it, the agent runs the missing steps, and the same action is then allowed."></video>
<button id="demo-video-toggle" type="button" aria-label="Play the demo video"
class="absolute right-3 bottom-3 inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-dark-950/85 border border-dark-700 text-xs font-mono text-slate-200 hover:text-white hover:border-brand-500/60 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-400">
<span id="demo-video-icon" aria-hidden="true">&#9654;</span><span id="demo-video-label">Play</span>
</button>
<div id="hero-example" class="min-w-0 rounded-xl bg-dark-950 border border-dark-700 p-4 font-mono text-xs sm:text-sm" aria-label="A blocked, then allowed, sequence">
<div class="text-[11px] text-slate-500 uppercase tracking-wider mb-3">// db-maintenance runbook</div>
<div class="space-y-1.5 overflow-x-auto">
<div class="sm:whitespace-nowrap"><span class="text-white">drop_prod_db</span> <span class="text-rose-400 font-bold">BLOCKED</span> <span class="text-slate-400">backup_validated missing</span></div>
<div class="sm:whitespace-nowrap"><span class="text-white">validate_backup</span> <span class="text-rose-400 font-bold">BLOCKED</span> <span class="text-slate-400">backup_taken missing</span></div>
<div class="sm:whitespace-nowrap"><span class="text-white">take_backup</span> <span class="text-brand-400 font-bold">ALLOWED</span></div>
<div class="sm:whitespace-nowrap"><span class="text-white">validate_backup</span> <span class="text-brand-400 font-bold">ALLOWED</span></div>
<div class="sm:whitespace-nowrap"><span class="text-white">drop_prod_db</span> <span class="text-brand-400 font-bold">ALLOWED</span></div>
</div>
<figcaption class="mt-3 text-xs text-slate-400 leading-relaxed">
43 seconds, silent, real output from <code class="text-slate-200">sop-mcp-server demo</code>.
<details class="mt-2">
<summary class="cursor-pointer text-slate-300 hover:text-white">Text version</summary>
<ol id="hero-example" class="mt-2 space-y-1 font-mono text-[12px] text-slate-300 list-decimal list-inside">
<li><span class="text-white">drop_prod_db</span> <span class="text-rose-400 font-bold">BLOCKED</span>, <code>backup_validated</code> is missing from the trace</li>
<li><span class="text-white">take_backup</span> <span class="text-brand-400 font-bold">ALLOWED</span></li>
<li><span class="text-white">validate_backup</span> <span class="text-brand-400 font-bold">ALLOWED</span></li>
<li><span class="text-white">drop_prod_db</span> <span class="text-brand-400 font-bold">ALLOWED</span>, the evidence now exists</li>
</ol>
</details>
</figcaption>
</figure>
<p class="text-[11px] text-slate-500 mt-3 leading-relaxed font-sans">The order is enforced by the barrier, whatever the agent says it already did.</p>
</div>
</div>
</section>

<!-- Intro video (muted so browsers allow autoplay) -->
<section id="demo-video" aria-label="Joltrin intro video" class="scroll-mt-20 mb-8 rounded-2xl overflow-hidden border border-dark-700/80 bg-dark-900">
<div class="relative w-full" style="aspect-ratio:16/9">
<iframe class="absolute inset-0 w-full h-full"
data-src="https://www.youtube-nocookie.com/embed/F0jYkBHJluI?autoplay=1&mute=1&loop=1&playlist=F0jYkBHJluI&playsinline=1&rel=0"
title="Joltrin intro video"
allow="autoplay; encrypted-media; picture-in-picture; fullscreen"
allowfullscreen
referrerpolicy="strict-origin-when-cross-origin"></iframe>
</div>
</section>

Expand Down Expand Up @@ -1763,39 +1763,12 @@ <h4 class="font-mono text-xs uppercase font-bold text-white mb-3">Open Source &a
<script>
let selectedBenchOps = 5000;

// Demo video. It stays unloaded until the page has finished loading, plays
// only while it is on screen, and does not autoplay for people who prefer
// reduced motion, who get the poster and a Play button instead. A phone gets
// the smaller file.
(function () {
const v = document.getElementById('demo-video-el');
const btn = document.getElementById('demo-video-toggle');
if (!v || !btn) return;
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const conn = navigator.connection || {};
const small = window.innerWidth <= 700 || conn.saveData;
const src = small ? './assets/joltrin-barrier-720.mp4' : './assets/joltrin-barrier.mp4';
let userPaused = false;
const label = (playing) => {
document.getElementById('demo-video-label').textContent = playing ? 'Pause' : 'Play';
document.getElementById('demo-video-icon').innerHTML = playing ? '&#10074;&#10074;' : '&#9654;';
btn.setAttribute('aria-label', playing ? 'Pause the demo video' : 'Play the demo video');
};
const play = () => { if (!v.getAttribute('src')) v.src = src; return v.play().catch(() => {}); };
v.addEventListener('play', () => label(true));
v.addEventListener('pause', () => label(false));
btn.addEventListener('click', () => {
if (v.paused) { userPaused = false; play(); } else { userPaused = true; v.pause(); }
});
if (reduce || !('IntersectionObserver' in window)) return;
window.addEventListener('load', () => {
new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) { if (!userPaused) play(); } else { v.pause(); }
});
}, { threshold: 0.4 }).observe(v);
});
})();
// The intro video player is about 5 MB of scripts. It starts after the page
// has loaded, so it never delays the first paint or the load event.
window.addEventListener("load", () => {
const frame = document.querySelector('#demo-video iframe[data-src]');
if (frame) frame.src = frame.dataset.src;
});

// Initialize Lucide Icons
document.addEventListener("DOMContentLoaded", () => {
Expand Down
Binary file removed docs/assets/joltrin-barrier-720.mp4
Binary file not shown.
Binary file removed docs/assets/joltrin-barrier-poster-540.jpg
Binary file not shown.
Binary file removed docs/assets/joltrin-barrier-poster.jpg
Binary file not shown.
Binary file removed docs/assets/joltrin-barrier.mp4
Binary file not shown.
63 changes: 63 additions & 0 deletions scripts/assemble-site.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Assembles the site exactly as the Pages deploy publishes it, then checks that
# every local file the pages point at is really in it. The deploy workflow and
# the pull request check both run this script, so what a PR proves is what ships.
# It expects demo/sop.wasm, demo-agents/sop-agents.wasm, both wasm_exec.js files
# and sop-arena/dist to be built already.
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"
SITE="${1:-_site}"

rm -rf "$SITE"
mkdir -p "$SITE/arena" "$SITE/agents" "$SITE/docs" "$SITE/assets"

# Technical demo (WASM) lives at the site root: joltrinhq.com/
cp demo/index.html demo/sop.wasm demo/wasm_exec.js demo/404.html demo/favicon.svg demo/favicon.ico demo/og-image.png demo/logo-mark.png "$SITE/"
# The documentation index lives at joltrinhq.com/docs/
cp docs/index.html "$SITE/docs/"
# The homepage loads its replay and videos from ./assets/. Every file there ships.
cp -r docs/assets/. "$SITE/assets/"
# SOP Arena lives at a subpath of the same site: joltrinhq.com/arena/
cp -r sop-arena/dist/. "$SITE/arena/"
# Agent verification barrier (verify in WASM) lives at joltrinhq.com/agents/
cp demo-agents/index.html demo-agents/sop-agents.wasm demo-agents/wasm_exec.js demo-agents/favicon.svg demo-agents/favicon.ico demo-agents/og-image.png demo-agents/logo-mark.png "$SITE/agents/"
# Preserve custom domain (e.g. joltrinhq.com) if CNAME exists
if [ -f CNAME ]; then
cp CNAME "$SITE/CNAME"
elif [ -f demo/CNAME ]; then
cp demo/CNAME "$SITE/CNAME"
fi

fail=0

# Every ./ path a page names must exist. This covers src, href and poster
# attributes and ./assets/ paths written inside scripts, which is how the demo
# video is loaded.
for page in index.html agents/index.html docs/index.html; do
dir="$(dirname "$page")"
refs=$(grep -oE "(src|href|poster)=[\"']\./[^\"'#?]+[\"']|['\"]\./assets/[^\"'#?]+['\"]" "$SITE/$page" \
| grep -oE '\./[^"'"'"'#?]+' | sed -E 's/^\.\///' | sort -u || true)
for ref in $refs; do
path="$SITE/$dir/$ref"; [ "$dir" = "." ] && path="$SITE/$ref"
if [ ! -e "$path" ]; then
echo "Error: $page references ./$ref but it is not in the site" >&2
fail=1
fi
done
done

# Videos are YouTube embeds only. A local video file or a <video> tag on a page
# once shipped without its files and showed a black box, so neither is allowed.
if grep -nE '<video|\.(mp4|webm|mov)' "$SITE/index.html" "$SITE/agents/index.html" "$SITE/docs/index.html"; then
echo "Error: pages must embed videos from YouTube, not a local video file or <video> tag" >&2
fail=1
fi
if ls "$SITE"/assets/*.mp4 "$SITE"/assets/*.webm "$SITE"/assets/*.mov >/dev/null 2>&1; then
echo "Error: video files must not be published in the site, use a YouTube link" >&2
fail=1
fi

[ "$fail" = 0 ] || exit 1
echo "Site assembled in $SITE and every referenced file is present."
44 changes: 0 additions & 44 deletions scripts/promo/build.sh

This file was deleted.

51 changes: 0 additions & 51 deletions scripts/promo/capture.mjs

This file was deleted.

Loading
Loading