Phases 11, 12: mcrctl CLI tool and mcr-web UI

Phase 11 implements the admin CLI with dual REST/gRPC transport,
global flags (--server, --grpc, --token, --ca-cert, --json), and
all commands: status, repo list/delete, policy CRUD, audit tail,
gc trigger/status/reconcile, and snapshot.

Phase 12 implements the HTMX web UI with chi router, session-based
auth (HttpOnly/Secure/SameSite=Strict cookies), CSRF protection
(HMAC-SHA256 signed double-submit), and pages for dashboard,
repositories, manifest detail, policy management, and audit log.

Security: CSRF via signed double-submit cookie, session cookies
with HttpOnly/Secure/SameSite=Strict, TLS 1.3 minimum on all
connections, form body size limits via http.MaxBytesReader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 10:14:38 -07:00
parent 185b68ff6d
commit 593da3975d
23 changed files with 3737 additions and 66 deletions

View File

@@ -0,0 +1,92 @@
{{define "title"}}Policies{{end}}
{{define "content"}}
<h1>Policy Rules</h1>
{{if .Error}}
<div class="error">{{.Error}}</div>
{{end}}
<h2>Create Policy Rule</h2>
<form method="POST" action="/policies">
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
<div class="form-row">
<div class="form-group">
<label for="priority">Priority</label>
<input type="number" id="priority" name="priority" value="100" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<input type="text" id="description" name="description" required>
</div>
<div class="form-group">
<label for="effect">Effect</label>
<select id="effect" name="effect">
<option value="allow">Allow</option>
<option value="deny">Deny</option>
</select>
</div>
<div class="form-group">
<label for="actions">Actions (comma-sep)</label>
<input type="text" id="actions" name="actions" placeholder="pull,push">
</div>
<div class="form-group">
<label for="repositories">Repositories (comma-sep)</label>
<input type="text" id="repositories" name="repositories" placeholder="*">
</div>
<button type="submit">Create</button>
</div>
</form>
<div id="policy-table">
{{if .Policies}}
<table>
<thead>
<tr>
<th>ID</th>
<th>Priority</th>
<th>Description</th>
<th>Effect</th>
<th>Actions</th>
<th>Repositories</th>
<th>Enabled</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .Policies}}
<tr id="policy-{{.Id}}">
<td>{{.Id}}</td>
<td>{{.Priority}}</td>
<td>{{.Description}}</td>
<td><span class="badge badge-{{.Effect}}">{{.Effect}}</span></td>
<td>{{joinStrings .Actions ", "}}</td>
<td>{{joinStrings .Repositories ", "}}</td>
<td>
{{if .Enabled}}
<span class="badge badge-enabled">Enabled</span>
{{else}}
<span class="badge badge-disabled">Disabled</span>
{{end}}
</td>
<td>
<form method="POST" action="/policies/{{.Id}}/toggle" style="display:inline;">
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
<button type="submit" class="small secondary">
{{if .Enabled}}Disable{{else}}Enable{{end}}
</button>
</form>
<form method="POST" action="/policies/{{.Id}}/delete" style="display:inline;">
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
<button type="submit" class="small danger">Delete</button>
</form>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<p>No policy rules configured.</p>
{{end}}
</div>
{{end}}