Files
noema/web/templates/pages/integration.html
T
Andy Dixon 89edbb7c06 Phase 13: integrations (research folder, git, Prometheus, signed webhooks)
Adds the integration layer that turns external sources into provenance-tagged
observations without ever executing what it reads.

- research folder poller: os.OpenRoot, text allowlist, binary/archive/symlink
  rejection, content fingerprints, duplicate tracking
- git poller: bare shallow mirror with hooks disabled and ext protocol refused,
  commit observations, co-change pairs
- Prometheus poller: threshold crossings and relative-change detection
- webhooks: HMAC over timestamp.body, +/-300s window, replay cache, 256 KiB cap,
  per-IP rate limit; refused entirely without a secret
- capabilities read_metrics (allow) and repository.read (ask), scoped to the
  invoking mind's integrations
- migrations 0013, API, UI pages, noemactl integrations, docs/integrations.md
2026-06-29 21:04:50 +01:00

15 lines
3.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
{{$i := .Data.Integration}}
<div class="pagehead mindhead"><div><p class="muted small"><a href="/integrations">Integrations</a> › {{$i.Kind}}</p><h1>{{$i.Name}} <span class="badge is-{{$i.Status}}">{{$i.Status}}</span></h1><p class="muted">Mind {{.Data.MindName}} · trust {{$i.Trust}} · every {{$i.Interval}}{{if $i.SecretSet}} · secret set{{end}}{{with $i.LastError}} · <span class="danger">{{.}}</span>{{end}}</p></div>
<div class="row">{{if and (.Principal.User.Role.AtLeast "operator") (ne $i.Kind "webhook")}}<form method="post" action="/integrations/{{$i.ID}}/run"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn">Run now</button></form>{{end}}
{{if .Principal.User.Role.AtLeast "administrator"}}<form method="post" action="/integrations/{{$i.ID}}/toggle"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn">{{if $i.Enabled}}Disable{{else}}Enable{{end}}</button></form>
<form method="post" action="/integrations/{{$i.ID}}/delete"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn btn-danger" data-confirm="Delete this integration? Ingested observations remain in history.">Delete</button></form>{{end}}</div></div>
<section class="grid cols-2">
<div class="card"><h2>Configuration</h2><pre class="json">{{pretty (jsonOf $i.Config)}}</pre>
{{if eq $i.Kind "webhook"}}<h3>Delivery</h3><p class="small">POST to <code>/api/v1/webhooks/{{$i.ID}}</code> with headers <code>X-Noema-Timestamp</code> (unix seconds) and <code>X-Noema-Signature: sha256=HMAC_SHA256(secret, timestamp + "." + body)</code>. Body: <code>{"title":"…","text":"…","reference":"…","salience":0.6}</code>. Unsigned deliveries and replays are refused.</p>{{end}}
{{if $i.State}}<h3>State</h3><pre class="json">{{pretty (jsonOf $i.State)}}</pre>{{end}}</div>
<div class="card"><h2>Recent runs</h2>{{if .Data.Runs}}<table class="table compact"><thead><tr><th>Started</th><th class="num">Items</th><th class="num">Observations</th><th class="num">Errors</th><th>Notes</th></tr></thead><tbody>{{range .Data.Runs}}<tr><td class="small muted nowrap">{{.StartedAt.Local.Format "01-02 15:04:05"}}</td><td class="num">{{.Items}}</td><td class="num">{{.Observations}}</td><td class="num {{if .Errors}}danger{{end}}">{{.Errors}}</td><td class="small">{{with .Error}}<span class="danger">{{.}}</span> {{end}}<span class="muted">{{trunc (printf "%s" .Notes) 160}}</span></td></tr>{{end}}</tbody></table>{{else}}<p class="muted">No runs yet.</p>{{end}}</div>
</section>
{{if .Data.Documents}}<div class="card"><h2>Ingested documents</h2><table class="table compact"><thead><tr><th>Status</th><th>Title</th><th>Type</th><th class="num">Bytes</th><th class="num">Chars</th><th>Reference</th><th></th></tr></thead><tbody>{{range .Data.Documents}}<tr><td><span class="badge {{if eq .Status "ingested"}}ok{{else if eq .Status "rejected"}}danger{{else}}{{end}}">{{.Status}}</span>{{with .Error}}<div class="small muted">{{.}}</div>{{end}}</td><td>{{.Title}}</td><td class="small">{{.ContentType}}</td><td class="num">{{.SizeBytes}}</td><td class="num">{{.ExtractedChars}}</td><td class="small mono">{{trunc .Reference 60}}</td><td>{{if .EventID}}<a class="small" href="/events/{{.EventID}}">event</a>{{end}}</td></tr>{{end}}</tbody></table></div>{{end}}
{{end}}