Branding (logos/colors), dashboard updates, and add model/provider explore page#121
Conversation
This comment has been minimized.
This comment has been minimized.
Create a logo for teep and rework the live dashboard around it, dependency-free (inline SVG/CSS/JS, system fonts only). Brand - Mark is a "held prompt": a rounded cradle (mint guards = teep) holding a chevron (light = your prompt, the thing that stays yours). Protective, not a checkpoint — a guard, not a gate. - docs/brand/ ships the mark, dark/light logo lockups, a palette + construction guide, and a dashboard preview. - README header uses <picture> + prefers-color-scheme so the dark-first lockup swaps to a light variant on GitHub's light theme. Dashboard - New "enclave graphite" palette and monospace-forward identity, off the generic GitHub-dark default. - Live attestation seal hero: the mark inside a factor-coverage ring that fills and recolors with posture (sealed / partial / blocked / idle). - Per-model factor drill-down grouped by tier, tier coverage bars, model filter, copy-to-clipboard for the base URL and model ids, and a client-side request-rate sparkline. - buildDashboardData now ships per-factor and per-tier data (dashFactor / dashTier) that the old page discarded. - Reduced-motion respected, responsive to mobile, visible focus. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Test the dashboard index, metrics, and SSE endpoints with HTML structure validation via golang.org/x/net/html parsing. Also fix exhaustive lint in tier rollup switch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moves the ~700-line raw string constant into dashboard.html and loads it via //go:embed. The Go file drops from 1139 to 443 lines, and the HTML gets proper syntax highlighting and editor tooling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New /test page lets users trigger attestation and E2EE inference tests from the browser. Models are fetched from /v1/models on page load, with Attest and Infer buttons on each model card. Attestation results show the full factor/tier breakdown; inference loops back through the proxy's own ServeHTTP to prove end-to-end functionality. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the union modelEntry (which mixed NEAR and Tinfoil fields) with properly typed per-provider structs: nearModelEntry, chutesModelEntry, veniceModelEntry, tinfoilModelEntry, and phalaModelEntry. Switch Chutes and Venice model listers from json.Unmarshal to jsonstrict.UnmarshalWarn for schema drift detection. Add validatingModelLister wrapper for Tinfoil and Phalacloud providers that use genericModelLister. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display context window, type, quantization, owner, capabilities, modalities, and description in model cards using data already flowing through /v1/models. All metadata renders conditionally based on what each provider includes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mark NEAR fields that aren't present on all model entries (quantization, hugging_face_id, is_ready, datacenters, openrouter) with omitempty so jsonstrict does not report them as missing. Add context_length to veniceModelEntry to match Venice's actual API schema. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Older Chutes models omit context_length, modalities, quantization, features, and permission. Tag these fields omitempty so jsonstrict does not report them as missing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tinfoil context_window and NEAR max_output_length are not present on all model entries. The top-level modelsResponse object field is absent from Phalacloud responses. Tag all three with omitempty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pinned handlers (nearcloud, neardirect) call BuildReport without setting Inapplicable, causing nvswitch_binding and sigstore_code_verified to incorrectly block NEAR models. Default to the standard inapplicable set when callers pass nil instead of requiring every call site to explicitly provide it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defense in depth: the nil-default safety net still applies DefaultInapplicableFactors, but now emits a warning so the programming error is visible in logs rather than silently corrected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
De-duplicate CSS and JS between dashboard and explore (formerly test) pages
using Go html/template partials. Rename /test to /explore to better reflect
the user-facing purpose of the page.
Key changes:
- Extract shared CSS into templates/_base_css.html and shared JS into
templates/_base_js.html using {{define}}/{{template}} composition
- Move HTML files into templates/ directory, serve via template.ExecuteTemplate
- Rename test → explore: routes, handlers, types, template names
- Add allow-fail factor yellow styling (warn class) distinct from enforced
failures (red) in both factor lists and tier bars
- Add provider metadata (pinned TLS, E2EE capable) pills on explore page
provider group headers via template-injected JSON
- Add E2EE active/capable/no distinction in explore inference results
- Add report timestamp display (relative time) after attestation
- Fix blocked pill CSS class collision (.block layout vs .pill.block)
- Add warned tier bar segment and dashTier.Warned field for allowed failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix double-escaping in tierBars: remove redundant esc() call on tier labels that were already escaped by the fallback path. - Add r.ok checks to all fetch() calls in explore.html so HTTP errors surface as "HTTP 4xx" messages instead of JSON parse failures. - Remove second-level card expand on explore model cards; card-body (pills, buttons, results) is now always visible when the provider group is expanded, matching dashboard behavior. - Replace json.Unmarshal with jsonstrict.UnmarshalWarn in explore.go to match the project's strict JSON parsing standard. - Delete dead FetchRekorProvenances (no production callers); update test to use FetchRekorProvenancesForPolicy with nil policy. - Replace raw strings.Cut with resolveModel + cacheModelFor in loopbackInfer E2EE cache lookup for consistency with inference path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
debae91 to
e370ddf
Compare
This comment has been minimized.
This comment has been minimized.
Cover the explore page success paths that were at 0-57% coverage: loopbackInfer (0% → 92%), handleExploreAttest (52% → 86%), handleExploreInfer (43% → 78%), handleExplorePage (57% → 64%). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adds a first-party, self-contained UI layer to teep serve (branding + dashboard overhaul) and introduces an “Explore” page for manually triggering provider/model attestation and a small inference smoke test.
Changes:
- Introduce shared embedded HTML templates + shared base CSS/JS for the serve dashboard UI.
- Add
/exploreUI plus POST endpoints to run on-demand attestation and a loopback inference test. - Expand model listing schema validation across providers (jsonstrict-based) and enrich dashboard attestation data (per-factor + per-tier rollups).
Reviewed changes
Copilot reviewed 24 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds brand logo lockup to README header. |
| internal/proxy/templates/explore.html | New Explore UI: model listing + “Attest/Infer” actions. |
| internal/proxy/templates/dashboard.html | New dashboard UI template with richer attestation/traffic visualization. |
| internal/proxy/templates/_base_js.html | Shared client JS helpers (escaping, factor/tier rendering, time formatting). |
| internal/proxy/templates/_base_css.html | Shared base styling + brand palette/tokens. |
| internal/proxy/templates.go | Embeds/parses templates via embed.FS and template.ParseFS. |
| internal/proxy/proxy.go | Registers /explore and swaps Phala/Tinfoil model listers to validating wrappers. |
| internal/proxy/explore.go | Implements Explore handlers + loopback inference logic. |
| internal/proxy/explore_internal_test.go | Tests Explore page rendering and infer/attest endpoints. |
| internal/proxy/dashboard.go | Switches index handler to template rendering + adds per-factor/per-tier data. |
| internal/proxy/dashboard_internal_test.go | Adds HTML rendering + inlined JSON validation tests for dashboard. |
| internal/provider/venice/models.go | Uses jsonstrict schema + capability filtering for Venice models. |
| internal/provider/venice/models_test.go | Updates Venice model fixtures for expanded schema coverage. |
| internal/provider/models.go | Adds provider-specific schemas + validating model lister wrapper and validators. |
| internal/provider/models_test.go | Updates NEAR fixtures and adds validating model lister test. |
| internal/provider/chutes/models.go | Uses jsonstrict schema + confidential_compute filtering for Chutes models. |
| internal/provider/chutes/models_test.go | Updates Chutes model fixtures for expanded schema coverage. |
| internal/config/update.go | Marks neardirect default as E2EE-capable. |
| internal/config/config.go | Sets neardirect env override to enable E2EE by default. |
| internal/config/config_test.go | Adjusts neardirect E2EE assertion message for clarity. |
| internal/attestation/report.go | Changes nil Inapplicable semantics to apply a default map + logs warning. |
| internal/attestation/rekor.go | Removes deprecated FetchRekorProvenances helper. |
| internal/attestation/rekor_test.go | Updates tests to use FetchRekorProvenancesForPolicy. |
| docs/brand/teep-mark.svg | Adds brand mark asset. |
| docs/brand/teep-logo.svg | Adds dark logo lockup asset. |
| docs/brand/teep-logo-light.svg | Adds light logo lockup asset. |
| docs/brand/README.md | Adds brand guidelines and usage instructions. |
| prov, upstreamModel, ok := s.resolveModel(req.Model) | ||
| if !ok { | ||
| http.Error(w, fmt.Sprintf("unknown model: %q", req.Model), http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
||
| report, _ := s.fetchAndVerify(ctx, prov, upstreamModel) | ||
| if report == nil { | ||
| http.Error(w, "attestation fetch failed; see server logs", http.StatusBadGateway) | ||
| return | ||
| } | ||
|
|
||
| s.cache.Put(prov.Name, upstreamModel, report) | ||
|
|
There was a problem hiding this comment.
Haha! I noticed this happening, but planned to debug later.
There was a problem hiding this comment.
Fixed in 1829660. handleExploreAttest now applies CacheKeySuffix to the context before fetchAndVerify and uses cacheModelFor for cache.Put.
| if _, _, ok := s.resolveModel(req.Model); !ok { | ||
| http.Error(w, fmt.Sprintf("unknown model: %q", req.Model), http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
There was a problem hiding this comment.
Fixed in 1829660. loopbackInfer E2EE lookup now applies CacheKeySuffix before the cache.Get so it finds the report under the correct suffixed key.
| result := rec.Result() | ||
| defer result.Body.Close() | ||
| respBody, _ := io.ReadAll(result.Body) | ||
|
|
||
| if result.StatusCode != http.StatusOK { | ||
| // Try to parse as a verification report (502 blocked response). | ||
| var report attestation.VerificationReport | ||
| if err := json.Unmarshal(respBody, &report); err == nil && report.Provider != "" { | ||
| return exploreInferResponse{ | ||
| Model: model, | ||
| Blocked: true, | ||
| Report: &report, | ||
| Error: fmt.Sprintf("attestation blocked (HTTP %d)", result.StatusCode), | ||
| } | ||
| } | ||
| return exploreInferResponse{ | ||
| Model: model, | ||
| Error: fmt.Sprintf("HTTP %d: %s", result.StatusCode, bytes.TrimSpace(respBody)), | ||
| } | ||
| } |
There was a problem hiding this comment.
Fixed in 1829660. Response body read is now capped to 1 MB via io.LimitReader, and echoed error bodies are truncated to 512 bytes.
Apply CacheKeySuffix in handleExploreAttest and loopbackInfer E2EE lookup so cache keys match the main proxy path (e.g. model@domain for tinfoil direct). Cap loopback response body read to 1 MB and truncate error messages to 512 bytes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Metrics Report
Details | | main (42f0339) | #121 (37257de) | +/- |
|---------------------|----------------|----------------|-------|
- | Coverage | 87.9% | 87.9% | -0.1% |
| Files | 86 | 87 | +1 |
| Lines | 10286 | 10416 | +130 |
+ | Covered | 9048 | 9157 | +109 |
- | Test Execution Time | 35s | 36s | +1s |Code coverage of files in pull request scope (87.3% → 87.2%)
Reported by octocov |

We needed a logo/color scheme, more details on the dashboard, and the ability to easily run model attestation/inference tests.
teep servedashboard, then expanded the features to increase information clarity (still more to go, but this is good enough to ship)I am specifically not using external JS libraries or external assets at this time.
Main Dashboard:

Explore:

Example Failure on Venice due to #113
