Files
noema/web/templates/layout.html
T
Andy Dixon a002a2c100 feat: add authentication, audit log, HTTP API and base web UI
Phase 1 foundation. Local auth per ADR-0006: Argon2id passwords, opaque
hashed session tokens with idle/absolute expiry, CSRF token plus Origin
check on cookie sessions, per-IP token bucket and per-account exponential
lockout, three roles enforced by middleware, personal API tokens for
noemactl. Audit log writer used by every admin mutation.

API: request ids, panic recovery, strict CSP and security headers, body
limits, single JSON error envelope, health/ready/status, users, sessions,
tokens, audit, token-protected /metrics.

UI: embedded html/template pages (login, first-run setup, dashboard,
system, users, account, audit, interpretation boundary), light/dark
theme, dependency-free app.js with CSRF-aware fetch and SSE helper.

Binaries: noemad with graceful shutdown, migrations at start, bootstrap
admin from environment, supervised background tasks; noemactl with
status, migrate, user create and meaningful exit codes.
2026-06-15 20:22:19 +01:00

61 lines
3.1 KiB
HTML

{{define "layout"}}<!doctype html>
<html lang="en" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{.CSRF}}">
<title>{{if .Title}}{{.Title}} · {{end}}Noema</title>
<link rel="stylesheet" href="/static/app.css">
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
<script src="/static/app.js" defer></script>
</head>
<body class="{{if .Principal}}app{{else}}bare{{end}}">
{{if .Principal}}
<a class="skip" href="#main">Skip to content</a>
<header class="topbar">
<button class="iconbtn nav-toggle" aria-label="Toggle navigation" aria-expanded="false" data-nav-toggle>☰</button>
<a class="brand" href="/"><span class="brand-mark" aria-hidden="true"></span>Noema</a>
<div class="topbar-spacer"></div>
<span class="muted small">{{.Principal.User.Username}} · {{.Principal.User.Role}}</span>
<button class="iconbtn" data-theme-toggle aria-label="Toggle colour theme" title="Toggle theme (t)">◐</button>
<form method="post" action="/logout" class="inline"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn btn-ghost small" type="submit">Sign out</button></form>
</header>
<div class="shell">
<nav class="sidenav" aria-label="Primary" data-nav>
{{template "nav" .}}
</nav>
<main id="main" class="content" tabindex="-1">
{{with .Flash}}<div class="flash flash-{{.Kind}}" role="status">{{.Message}}</div>{{end}}
{{template "content" .}}
</main>
</div>
{{else}}
<main id="main" class="bare-main">
{{with .Flash}}<div class="flash flash-{{.Kind}}" role="status">{{.Message}}</div>{{end}}
{{template "content" .}}
</main>
{{end}}
<footer class="footer muted small">Noema {{.Version}} · models cognitive processes; it does not claim consciousness. <a href="/about">Interpretation boundary</a></footer>
</body>
</html>{{end}}
{{define "nav"}}
<div class="navgroup"><div class="navhead">Observe</div>
<a href="/" {{if eq .Path "/"}}aria-current="page"{{end}}>Dashboard</a>
<a href="/minds" {{if hasPrefix .Path "/minds"}}aria-current="page"{{end}}>Minds</a>
<a href="/approvals" {{if hasPrefix .Path "/approvals"}}aria-current="page"{{end}}>Approvals</a>
</div>
<div class="navgroup"><div class="navhead">Configure</div>
<a href="/llm" {{if hasPrefix .Path "/llm"}}aria-current="page"{{end}}>LLM</a>
<a href="/prompts" {{if hasPrefix .Path "/prompts"}}aria-current="page"{{end}}>Prompts</a>
<a href="/integrations" {{if hasPrefix .Path "/integrations"}}aria-current="page"{{end}}>Integrations</a>
<a href="/policies" {{if hasPrefix .Path "/policies"}}aria-current="page"{{end}}>Policies</a>
</div>
<div class="navgroup"><div class="navhead">System</div>
<a href="/system" {{if eq .Path "/system"}}aria-current="page"{{end}}>System</a>
<a href="/audit" {{if eq .Path "/audit"}}aria-current="page"{{end}}>Audit</a>
{{if .Principal.User.Role.AtLeast "administrator"}}<a href="/admin/users" {{if hasPrefix .Path "/admin/users"}}aria-current="page"{{end}}>Users</a>{{end}}
<a href="/account" {{if eq .Path "/account"}}aria-current="page"{{end}}>Account</a>
</div>
{{end}}