{section.title}
+-
+ {section.body.map((item) => (
+
- {item} + ))} +
diff --git a/src/app/globals.css b/src/app/globals.css
index c69e4d5..4b6e41e 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -198,6 +198,23 @@ select {
border-bottom: 1px solid var(--border);
}
+.nav-text-link {
+ color: var(--text-muted);
+ font-size: 0.9rem;
+ font-weight: 600;
+ line-height: 1.4;
+ transition: color 150ms ease;
+}
+
+.nav-text-link:hover {
+ color: var(--text-primary);
+}
+
+.nav-text-link:focus-visible {
+ outline: 2px solid color-mix(in srgb, var(--accent) 48%, transparent);
+ outline-offset: 4px;
+}
+
/* ── Full-bleed editorial sections ── */
.home-hero-section {
padding-top: 2rem;
@@ -244,6 +261,27 @@ select {
}
}
+.site-footer {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ border-top: 1px solid var(--border);
+ padding-top: 1.25rem;
+ color: var(--text-soft);
+ font-family: var(--font-mono), monospace;
+ font-size: 0.78rem;
+}
+
+.site-footer a {
+ color: var(--text-muted);
+}
+
+.site-footer a:hover {
+ color: var(--text-primary);
+}
+
/* ── Bento grid — tonal separation via 1px gaps ── */
.bento-grid {
display: grid;
diff --git a/src/app/security/page.tsx b/src/app/security/page.tsx
new file mode 100644
index 0000000..ad762a1
--- /dev/null
+++ b/src/app/security/page.tsx
@@ -0,0 +1,107 @@
+import Link from "next/link";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Security - agent-render",
+ description: "Security notes for agent-render static artifact links, markdown rendering, Mermaid, CSP, and reports.",
+};
+
+const sections = [
+ {
+ title: "What reaches the server",
+ body: [
+ "Static mode sends HTML, CSS, and JavaScript to the browser. Artifact payloads are not sent to the static host as part of the initial page request.",
+ "Fragment payloads stay out of the HTTP request path, query string, and request body for the static host.",
+ "The server can still receive normal static asset requests, IP address, user agent, referrer headers, and access logs from the hosting layer.",
+ ],
+ },
+ {
+ title: "What can still leak",
+ body: [
+ "agent-render is zero-retention by host design. It is not a secret manager.",
+ "Artifact contents can still leak through copied URLs, browser history, bookmarks, screenshots, screen sharing, crash reports, extensions, referrer behavior, and future client-side analytics if someone adds them.",
+ "Do not put secrets, credentials, private keys, production tokens, or regulated data in artifact links.",
+ ],
+ },
+ {
+ title: "Markdown and Mermaid",
+ body: [
+ "Markdown artifacts are rendered as GitHub-flavored Markdown and passed through rehype-sanitize before display.",
+ "React Markdown is configured with skipHtml, so raw HTML embedded in markdown is skipped instead of rendered.",
+ "Mermaid diagrams are only rendered from fenced mermaid code blocks. Mermaid runs with securityLevel: \"strict\" and falls back to showing source text if rendering fails.",
+ ],
+ },
+ {
+ title: "CSP and security headers",
+ body: [
+ "The default static export does not require a runtime server. Configure Content-Security-Policy and other security headers at your static host or CDN.",
+ "Recommended headers include a restrictive Content-Security-Policy, Referrer-Policy, X-Content-Type-Options, Permissions-Policy, and HSTS when served over HTTPS.",
+ "If you loosen CSP for a custom deployment, review markdown, Mermaid, fonts, images, and script sources together before publishing.",
+ ],
+ },
+ {
+ title: "Known limitations",
+ body: [
+ "URL fragments are client-side, but they are still visible to the browser, local machine, extensions, and anyone who receives the link.",
+ "Self-hosted UUID mode is a different deployment mode and stores payloads server-side by design.",
+ "The viewer treats payloads as untrusted input, but the safest policy is to keep sensitive material out of links entirely.",
+ ],
+ },
+] as const;
+
+/**
+ * Public security page documenting the static host boundary and renderer safety posture.
+ * Keeps the copy direct and linkable for operators, reviewers, and security reports.
+ */
+export default function SecurityPage() {
+ return (
+ Public security notes
+ agent-render is a static artifact viewer. Its core host boundary is simple: artifact data lives in the
+ URL fragment, so the static host does not receive it as part of the initial page request.
+ Reports
+ Report security issues through the GitHub repository. Use a private vulnerability report when available;
+ otherwise open a minimal issue asking for a private contact path and do not include exploit details in public.
+ Security
+ {section.title}
+
+ {section.body.map((item) => (
+
+ Security contact
+
{activeArtifact ? `${getArtifactSubtitle(activeArtifact)} selected.` - : "Select a fragment above to render it here. Everything stays in the URL."} + : "Select a fragment above to render it here. Payloads stay off the host request path, but links still need care."}
{step}
))} -Security
-- The payload never leaves the URL hash. Rendering is entirely client-side. + Read the security page +
+ Fragment payloads stay out of the static host request path, but links are not secret-safe.
-Hosting
@@ -826,6 +833,11 @@ export function ViewerShell() { )} + +