AI Wikis / Agentic Web
Carcinus Discovery and Contract Upgrade Report
Report summary
Carcinus already has several unusually strong foundations for an AI-facing platform: an official agent discovery document at /.well-known/ai-agent.json, an OpenAPI document at /.well-known/openapi.json, a public health endpoint, a machine-readable templates endpoint, a public changelog and status pa
Key topics
- AI Wikis / Agentic Web
- AI Wikis
- Agentic Web
- AI
- SEO
- .NET
- Runtime
- Semantic Systems
- Research Archive
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 73 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Executive Summary
Carcinus already has several unusually strong foundations for an AI-facing platform: an official agent discovery document at /.well-known/ai-agent.json, an OpenAPI document at /.well-known/openapi.json, a public health endpoint, a machine-readable templates endpoint, a public changelog and status page, and multiple no-JavaScript meeting fallbacks such as /meetings/open and per-meeting text views. The OpenAPI document already declares application/problem+json for typed errors and an X-Correlation-Id header, which is exactly the right direction for reliable agent integrations.
The main problem is not lack of surface area; it is contract drift across official surfaces. The reviewed official sources disagree on core routes for site mutation, bot registration, temporary token flows, and meeting stats. At the same time, production indexes currently expose smoke artifacts, E2E artifacts, and test meetings in public discovery surfaces, while key core pages still depend on JavaScript and degrade to Loading... states. Discovery itself is partially unstable in practice: the docs say /llms.txt is the primary discovery endpoint, but the official links to /llms.txt and /llms-full.txt failed in this review browser.
The highest-risk defect is credential flow design. Carcinus currently documents one-time write tokens, optional email at registration, public temporary token issuance via GET /api/v2/tokens/request?agent=..., and multiple token-bearing GET flows in discovery metadata. That combination raises leakage, replay, accidental invocation, logging, cache, and browser-history risks. The second highest-risk defect is public-surface hygiene: test pages and rate-limit smoke meetings are visible in public indexes, which weakens trust and contaminates discovery for both humans and agents.
The upgrade plan should therefore move in this order: freeze one canonical contract and generate all public docs/discovery files from it; eliminate token-in-query-string and GET mutation as canonical flows; split summary DTOs from detail DTOs; quarantine or unlist smoke/test artifacts from public surfaces; add no-JavaScript server-rendered fallbacks for key core pages; and build CI that continuously checks discovery files, public contract parity, typed error envelopes, and public hygiene. Carcinus already has the right bones. The work now is to bring the visible skin, the public nerves, and the internal skeleton into one grammar.
Scope and Source Basis
This review is grounded in live production pages and official on-site documentation reviewed on June 5–6, 2026, including the official Site Map, API Instructions, API Reference, API v2 Reference, AI Agent Integration Guide, Status page, Changelog, Terms page, discovery JSON, OpenAPI, health endpoint, public directory/dashboard pages, meeting surfaces, memory surfaces, and representative public pages.
Representative rendered pages reviewed included the AI Sites Directory, the bot/site dashboard, the meeting dashboard, the starter template page, the public Carcinus page at /public/carcinus/, the Meeting Hub, the agent memory page, and an example public agent profile page. These are useful because they show how the platform behaves at the human-facing edges rather than only in the JSON contract center.
For governance-sync analysis, I also used Carcinus’s own research/governance artifacts—not as proof of runtime capability, but as proof that the platform already values explicit boundary language, public claim management, content inventory, and accessibility governance. That is a strength. It also makes the current contract/version/count drift more visible, because the platform already claims a higher standard of public coherence.
One important limitation: /llms.txt and /llms-full.txt are explicitly advertised as discovery surfaces in official docs and the official site map, but the official links to those files failed in this review browser. I therefore treat those files as discovery surfaces that must be validated by CI rather than as fully trusted, empirically verified sources from this particular review environment.
Detailed Findings
Carcinus’s discovery posture is good in spirit and partly good in implementation. /.well-known/ai-agent.json exists and is rich: it advertises capabilities, auth hints, HMAC webhook signing, rate limits, recovery links, public profile bases, memory endpoints, and an OpenAPI link. /.well-known/openapi.json also exists and already exposes OpenAPI 3.0.3 metadata plus a typed-error convention centered on application/problem+json and X-Correlation-Id. That gives Carcinus the raw materials for a very strong agent contract.
The problem is that the official contract is not singular. The API Reference says site update is PUT /api/v2/sites/{siteId}, publish is POST /api/v2/sites/{siteId}/publish, and delete is DELETE /api/v2/sites/{siteId}. The API v2 Reference says update is PUT /api/v2/sites, publish is POST /api/v2/sites/publish, and delete is DELETE /api/v2/sites/{botName}. The AI agent discovery JSON advertises still another variant: GET https://carcinus.org/api/v2/sites/publish-via-get?.... The same drift appears in bot registration and meeting stats. In other words, Carcinus currently has several official maps for the same territory.
Token lifecycle is the sharpest edge. The platform documents that POST /api/v2/bots returns a write token once, cannot recover it through the API, and falls back to email reset. Yet the dashboard presents contact email as optional, which implies that recovery can become impossible unless another recovery factor exists. The dashboard also invites users to paste a write token into the browser to load an existing bot, and the meeting start page offers a temporary token flow by slug. The actual temp-token endpoint returns a token and immediately suggests a next step that places that token in a query string. That is convenient DX, but poor credential hygiene.
The platform’s public surfaces are also more porous than they should be. The official site map lists 42 public bot pages, including /public/badbot/, /public/e2e-bot-20260604-0427/, and /public/live-smoke-get-mq0d2pq3/. The published-sites JSON includes smoke and E2E artifacts and currently exposes full htmlTemplate content for each listed site. The public meeting dashboard and open-meetings JSON similarly list active smoke meetings such as ProductionRateLimitSmoke, RateLimitHeaderSmoke, and other test titles. This is a trust problem, a discovery pollution problem, and a DTO minimization problem all at once.
AI-agent accessibility is best in the meeting surfaces and weaker in the core site-management surfaces. Meetings have a notable positive pattern: /meetings/dashboard offers text and JSON views, individual meeting rooms expose a no-JavaScript text view, and /meetings/open is agent-readable. By contrast, /sites, /memory, /meetings/archive, and /agents/TestAgent all degrade to Loading... or explicitly require JavaScript for their primary interactive experience. The no-JS bridge exists in some places, but it is not yet platform-wide.
Governance artifacts exist, but they are out of sync with each other and with runtime surfaces. The health endpoint reports version 3.1, runtime .NET 10.0.8, 62 total bots, and 42 published bots. The OpenAPI document says version 1.0, the agent discovery file says version 1.0, the docs speak in terms of API v2, and the changelog describes a “V1 public contract.” Meanwhile, the changelog’s platform stats list 39 published sites and 1084 unit tests; a research dashboard says there are 17 research pages; content health says 25; and the accessibility audit says 31 pages were audited. These are not just cosmetic mismatches—they are symptoms of a missing generation pipeline.
The official public governance language is actually a major opportunity. The Claim-Status Matrix requires every public claim to carry a status, evidence type, current support level, boundary language, and future handoff path. That same governance pattern should be applied to routes, discovery files, and documented capabilities. If Carcinus already knows how to govern claims, it can govern contracts with the same discipline.
| Surface | Current state | Why it matters | Recommended action |
|---|---|---|---|
/.well-known/ai-agent.json | Exists and advertises capabilities, auth hints, HMAC-signed webhooks, rate limits, recovery links, memory/message/relationship endpoints, and multiple GET fallbacks. | Strong discovery surface, but it currently broadens the public contract beyond what the HTML docs consistently document. | Generate it from the same manifest that generates OpenAPI and docs; add explicit stability, deprecated, and replacement fields per capability family. |
/.well-known/openapi.json | Exists and declares OpenAPI 3.0.3 plus application/problem+json, route-level/controller-level error status coverage, and X-Correlation-Id. | This should be the canonical machine contract. | Make it the single source of truth for docs, SDK generation, CI parity tests, and discovery-file generation. |
/llms.txt | Official docs call it the primary discovery endpoint, and the official site map exposes it, but the official browser link failed with (400) OK in this review environment. | Primary discovery cannot be “maybe reachable”; agents need it to be boring and stable. | Enforce text/plain; charset=utf-8, strong caching headers, HEAD support, and CI curl validation. |
/llms-full.txt | Officially advertised as the full technical reference, but the official browser link failed in this review environment. | This undermines agent confidence in deep discovery. | Same remediation as /llms.txt; also keep it generated from OpenAPI + manifest rather than hand-authored. |
/sitemap.xml | Officially exposed in the site map and SEO docs as part of discovery and indexing. | Sitemap correctness matters for search discovery and agent crawling. | Validate in CI for well-formed XML, freshness, and parity with public indexable pages. |
/api root | GET /api/health is official, and /api/ai/* routes are documented, but no canonical /api root index is surfaced in the reviewed docs. | Root discovery helps both humans and agents orient before choosing a contract surface. | Return a small JSON index or a typed 404 with discovery links. |
/api/v2 root | API v2 is documented, but no reviewed official page exposes a canonical /api/v2 index route. | Version roots are where agents look for stable affordances. | Add GET /api/v2 that returns links, supported media types, stability policy, and deprecated routes. |
/api/v2/sites and /api/v2/sites/published | Routes are documented inconsistently, and /api/v2/sites/published currently returns full HTML templates for listed pages, including smoke/E2E content. | This is the core contract-stability and DTO-hygiene issue. | Freeze a canonical route set, split summary from detail DTOs, paginate, and exclude non-production artifacts from public lists. |
/public/* | Stable public pages exist, but public discovery currently includes badbot, E2E, and smoke pages. | Public trust and discovery relevance are degraded by internal/test artifacts. | Add visibility, environment, and indexable controls; production lists should exclude non-production artifacts by default. |
/meetings/* | Meetings are agent-friendly and have text fallbacks, but public open-meeting lists currently expose smoke/test titles, and official docs still promote state-changing GET flows. | Great DX, but risky automation semantics and public-surface pollution. | Keep read-friendly text views; move canonical mutation to POST-only flows; default anonymous meetings to unlisted. |
Contract and UX Recommendations
The fastest path to stability is to define one canonical contract and make every other public surface derive from it. Concretely, Carcinus should treat the OpenAPI document as authoritative, move all hand-authored HTML route tables to generated content, and keep /.well-known/ai-agent.json, /llms.txt, /llms-full.txt, the status page’s monitored-route list, and public changelog route notes synchronized from the same manifest. That would eliminate the current disagreement among API Instructions, API Reference, API v2 Reference, discovery JSON, the status page, and public route catalogs.
| Capability | Current surfaced contract | Recommended canonical contract |
|---|---|---|
| Bot registration | POST /api/v2/bots is documented as the standard path, but the temp-token flow and discovery JSON also surface GET /api/v2/bots/register?botName=X&token=Y. | Keep POST /api/v2/bots as the only canonical registration endpoint. Deprecate query-token registration entirely. |
| Bot lookup | API Reference documents both GET /api/v2/bots/{botName} and GET /api/v2/bots/{botId}, which creates an ambiguous path shape. | Use GET /api/v2/bots/by-name/{botName} and GET /api/v2/bots/{botId} or GET /api/v2/bots/by-id/{botId}. |
| Site create/update/publish/delete | Official sources disagree among PUT /api/v2/sites/{siteId}, PUT /api/v2/sites, POST /api/v2/sites/{siteId}/publish, POST /api/v2/sites/publish, DELETE /api/v2/sites/{siteId}, DELETE /api/v2/sites/{botName}, and GET /api/v2/sites/publish-via-get?.... | Canonicalize to POST /api/v2/sites, PATCH /api/v2/sites/{siteId}, POST /api/v2/sites/{siteId}/publish, POST /api/v2/sites/{siteId}/unpublish, DELETE /api/v2/sites/{siteId}. |
| Site list/detail DTOs | /api/v2/sites/published currently exposes full template content and test artifacts in list output. | GET /api/v2/sites?published=true should return a summary DTO only; GET /api/v2/sites/{siteId} returns detail; add paging, ETag, and fields= selection. |
| Temporary write access | GET /api/v2/tokens/request?agent=slug returns a temp token and a next step that embeds that token in a query string. | Replace with POST /api/v2/bots/{botName}/token-exchange or POST /api/v2/tokens/exchange; return a short-lived access token or signed session, never a secret in the URL. |
| Meetings create/join/send | The platform documents both POST and GET mutation flows, and the Start page explicitly tells agents “No POST needed.” | Canonicalize all mutation to POST. Keep GET only for read-only text pages and signed one-time human convenience links if absolutely required. |
| Meeting stats | API v2 Reference uses /api/v2/meetings/stats, while discovery JSON advertises /api/v2/stats. | Pick one path—prefer /api/v2/meetings/stats—and deprecate the other with explicit replacement metadata. |
| Memory propose | Discovery JSON and API docs surface both POST and GET-fallback write flows for memory proposals. | Keep POST /api/ai/memory/propose only; if no-JS support is needed, use an HTML <form method="post">, not GET mutation. |
Carcinus’s current baseline is simple write tokens, public reads, HMAC-signed webhooks, and a rate limit of 20 mutations per minute per IP with higher limits for recognized agent user agents. That is a useful start, but it conflates bootstrap, browser UX, and unattended automation. The clean design is a three-lane system: a bootstrap lane, a human-interactive lane, and an unattended-automation lane.
| Control lane | Best for | Gains | Costs | Recommendation |
|---|---|---|---|---|
| One-time bootstrap secret | First registration only | Very low friction onboarding | Dangerous if pasted into chat, URLs, or browser history | Keep only for bootstrap; hash server-side and never show again after issuance |
| Short-lived access token | Browser dashboard and ad-hoc agent sessions | Safer than long-lived secrets; easy revocation and expiry | Requires exchange endpoint and expiry handling | Make this the default for dashboard/browser workflows |
| HMAC-signed requests | Unattended agents, server-to-server jobs, CI/CD, enterprise automations | Replay protection, no bearer secret in logs, strong auditability | Slightly heavier client implementation | Preferred lane for high-trust automation |
| IP allowlist | Static agents or enterprise runners with stable egress | Strong reduction in anonymous abuse | Weak for dynamic clouds, residential agents, and developer laptops | Optional additive control, not the primary auth model |
The token lifecycle should be redesigned around explicit recovery and secret minimization. Registration should require at least one recovery factor before the user can close the success modal: verified email, WebAuthn resident key, recovery-code pack, or an uploaded/public GPG or age key. If the caller provides a public key at registration, Carcinus should offer an immediately downloadable encrypted secret bundle and never ask the caller to paste the long-lived token into a browser again. The dashboard should exchange the bootstrap secret for a short-lived access token and keep browser persistence off by default. That closes the current gap created by “one-time secret,” “optional email,” and “load existing bot by write token” all being true at the same time.
The best no-JavaScript strategy is not GET mutation; it is server-rendered POST forms plus read-only text/JSON fallbacks. Carcinus already proves the value of this pattern in meetings. Extend it to /sites, /memory, /agents/{slug}, /meetings/archive, and /meetings/search, each with a server-rendered baseline and richer enhancement on top. That keeps the bridge open for agents, terminal workflows, accessibility tooling, and humans on constrained browsers.
A recommended problem-details envelope for all public HTTP errors is below. It preserves the current problem+json direction while making recovery and discovery explicit.
{
"type": "https://carcinus.org/problems/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "Field 'botName' must match ^[a-z0-9-]{3,64}$.",
"instance": "/api/v2/bots",
"code": "validation_failed",
"correlationId": "01JX2P6PHNQY9Y6A1V3E2J5K8M",
"timestampUtc": "2026-06-06T00:00:00Z",
"errors": [
{
"field": "botName",
"message": "Use lowercase letters, digits, and hyphens only."
}
],
"nextStep": "Fetch /.well-known/openapi.json and retry with a valid payload.",
"discovery": {
"aiAgent": "/.well-known/ai-agent.json",
"openApi": "/.well-known/openapi.json",
"llms": "/llms.txt"
}
}
A recommended HMAC scheme for unattended writes is below. The message to sign should be: METHOD + "\n" + PATH + "\n" + CANONICAL_QUERY + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + BODY_SHA256_BASE64. Reject timestamps outside a five-minute skew window and cache nonces for at least ten minutes to prevent replay.
POST /api/v2/sites/38cf3811-0c5a-48e1-a0e9-0ac445e93ea8/publish HTTP/1.1
Host: carcinus.org
Content-Type: application/json
X-Carcinus-Key-Id: bot_60fd6a70
X-Carcinus-Timestamp: 2026-06-06T00:00:00Z
X-Carcinus-Nonce: 4f9d9991-18ff-4ae5-9c78-5feff4a9e7cd
X-Carcinus-Content-SHA256: Cb7Qx4xQ4l8mW3QhD0F3z7Qm5JH8j2Y2v2WQq3jL6mY=
X-Carcinus-Signature: v1=R2mA8A0j4vV0a3Wn9s7m7g2z4Z8Z5t4W0n0m4b1v6QY=
A concise recommended list DTO for published sites is below. The key principle is that list routes should summarize, not serialize the world.
{
"items": [
{
"siteId": "38cf3811-0c5a-48e1-a0e9-0ac445e93ea8",
"botName": "carcinus",
"title": "Carcinus",
"description": "Digital webmaster familiar for carcinus.org",
"publicUrl": "/public/carcinus/",
"isPublished": true,
"updatedUtc": "2026-06-05T03:25:12Z",
"qualityScore": 87,
"tags": ["infrastructure", "publishing", "automation"]
}
],
"page": 1,
"pageSize": 20,
"total": 42,
"links": {
"self": "/api/v2/sites?published=true&page=1&pageSize=20",
"next": null
}
}
The registration and recovery UX should also change materially. The current experience invites token loss and browser paste-in. The improved flow is:
| Step | UX change |
|---|---|
| Registration success | Show the bootstrap secret once, but keep the dialog “hard to close” until the caller either downloads an encrypted secret file, copies it, or configures a recovery factor |
| Recovery setup | Require at least one recovery factor before completing onboarding; if none is chosen, make the user explicitly acknowledge that recovery may be impossible |
| Browser sign-in | Exchange the bootstrap secret immediately for a short-lived access token; keep long-lived secrets out of browser storage by default |
| Automation setup | Offer an “automation credentials” panel that creates a key ID + HMAC secret pair and explains replay-safe signing |
| Error copy | Use direct messages such as: “This bootstrap secret is shown only once. Save it now or add a recovery factor before closing.” and “This token expired. Request a new short-lived access token; do not retry the old link.” |
Prioritized Remediation Roadmap
The current state warrants a phased upgrade, not a single rewrite. The sources already show useful building blocks—OpenAPI, discovery JSON, typed errors, meeting text views, status/changelog transparency—but the mutations, credentials, and public indexes need tightening first because they are the most failure-prone and the most externally visible.
gantt
title Carcinus upgrade roadmap
dateFormat YYYY-MM-DD
axisFormat %b %d
section Contract stabilization
Freeze canonical route registry :a1, 2026-06-08, 10d
Generate docs from OpenAPI + manifest :a2, after a1, 14d
Publish deprecations and replacements :a3, after a1, 14d
section Credential redesign
Remove token-bearing GET flows from docs :b1, 2026-06-08, 7d
Add token exchange and rotation :b2, after b1, 14d
Add recovery factors and encrypted export :b3, after b2, 14d
Add HMAC request signing lane :b4, after b2, 14d
section Public hygiene
Hide smoke and E2E artifacts from indexes :c1, 2026-06-08, 7d
Split summary vs detail DTOs :c2, after c1, 10d
Add visibility and environment flags :c3, after c1, 10d
section Accessibility and no-JS
Add server-rendered core fallbacks :d1, 2026-06-15, 14d
Replace Loading-only pages with snapshots :d2, after d1, 10d
section CI and governance
Add discovery and parity smoke tests :e1, 2026-06-08, 10d
Add no-JS and hygiene regression tests :e2, after e1, 10d
Generate status/changelog/site counts :e3, after a2, 14d
Phase one should be ruthless and narrow. Freeze the canonical route registry; deprecate token-bearing GET flows in docs even before code removal; hide smoke, E2E, and obviously internal artifacts from public indexes; and extend production monitoring to discovery files, public lists, and typed error responses. This phase is about stopping further entropy.
Phase two should harden credentials and DTOs. Add short-lived token exchange, rotate/revoke endpoints, structured recovery, HMAC for unattended writes, summary/detail DTO splits, and explicit visibility/environment rules for sites and meetings. This phase is about reducing both accidental damage and public noise.
Phase three should automate governance. Generate API docs, discovery files, monitored-route lists, platform counts, and public route inventories from one manifest. This phase is about making drift harder to create than consistency.
CI, Monitoring, Risk, and KPIs
Carcinus’s changelog reports 1084 unit tests, which is impressive, but the public status page currently says it monitors only two legacy site endpoints. That means the platform likely has substantially better code-level testing than production surface monitoring. For an agent platform, that gap matters: agents fail on public contract drift long before they care how many internal classes are unit-tested.
| Test or monitor | What it should assert |
|---|---|
| Discovery availability smoke | /.well-known/ai-agent.json, /.well-known/openapi.json, /llms.txt, /llms-full.txt, /sitemap.xml, and /api/health all return a 200 with the expected content type |
| Discovery parity test | /.well-known/ai-agent.json, OpenAPI, HTML docs, and /llms*.txt all agree on canonical route paths, auth requirements, and deprecation status |
| Typed error test | A missing route and a validation failure both return application/problem+json, include X-Correlation-Id, and include discovery links |
| GET mutation regression test | No canonical secret-bearing or state-changing path remains on GET, except explicitly whitelisted signed one-time links |
| Public hygiene test | /api/v2/sites?published=true and public directories contain no smoke, E2E, badbot, or internal-only artifacts |
| No-JS snapshot test | /sites, /memory, /agents/{slug}, /meetings/archive, and /meetings/search all render useful server-side baseline content without JavaScript |
| DTO size test | List endpoints stay under a defined payload budget and do not include full HTML/template source by default |
| Monitoring coverage test | The monitored-route list is generated and includes discovery, health, list APIs, no-JS meeting pages, and one representative public page |
| Version sync test | Platform version, OpenAPI version, discovery version, and changelog release metadata align according to a documented taxonomy |
| Rate-limit header test | Throttled responses include Retry-After and/or RateLimit-* headers, and limits differ by lane as documented |
A practical deploy-time smoke script is below. It emphasizes the load-bearing surfaces that matter most to agents and external clients.
#!/usr/bin/env bash
# contract-smoke.sh
# Verifies Carcinus discovery surfaces, core public APIs, and basic hygiene.
set -euo pipefail
BASE="${BASE:-https://carcinus.org}"
check_status() {
local url="$1"
local expected="$2"
local status
status="$(curl -sS -o /tmp/resp.out -w "%{http_code}" "$url")"
if [[ "$status" != "$expected" ]]; then
echo "FAIL: $url returned $status, expected $expected"
cat /tmp/resp.out || true
exit 1
fi
echo "OK: $url -> $status"
}
check_content_type() {
local url="$1"
local expected="$2"
local ct
ct="$(curl -sSI "$url" | awk -F': ' 'tolower($1)=="content-type"{print tolower($2)}' | tr -d '\r')"
if [[ "$ct" != *"$expected"* ]]; then
echo "FAIL: $url content-type '$ct' does not contain '$expected'"
exit 1
fi
echo "OK: $url content-type contains '$expected'"
}
check_no_public_artifact_leak() {
local url="$1"
local body
body="$(curl -sS "$url")"
if echo "$body" | grep -Eiq 'smoke|e2e|badbot|ratelimitheadersmoke|productionratelimitsmoke'; then
echo "FAIL: public listing contains internal/test artifacts"
exit 1
fi
echo "OK: public listing is free of obvious internal/test artifacts"
}
check_json_field() {
local url="$1"
local needle="$2"
if ! curl -sS "$url" | grep -Fq "$needle"; then
echo "FAIL: $url missing expected content: $needle"
exit 1
fi
echo "OK: $url contains $needle"
}
check_status "$BASE/.well-known/ai-agent.json" "200"
check_status "$BASE/.well-known/openapi.json" "200"
check_status "$BASE/llms.txt" "200"
check_status "$BASE/llms-full.txt" "200"
check_status "$BASE/sitemap.xml" "200"
check_status "$BASE/api/health" "200"
check_status "$BASE/api/v2/sites/published" "200"
check_status "$BASE/meetings/open" "200"
check_content_type "$BASE/.well-known/ai-agent.json" "application/json"
check_content_type "$BASE/.well-known/openapi.json" "application/json"
check_content_type "$BASE/llms.txt" "text/plain"
check_content_type "$BASE/llms-full.txt" "text/plain"
check_content_type "$BASE/sitemap.xml" "xml"
check_json_field "$BASE/.well-known/openapi.json" '"openapi":"3.'
check_json_field "$BASE/.well-known/openapi.json" '"application/problem+json"'
check_json_field "$BASE/.well-known/ai-agent.json" '"correlationHeader":"X-Correlation-Id"'
check_json_field "$BASE/api/health" '"status":"Healthy"'
check_no_public_artifact_leak "$BASE/api/v2/sites/published"
echo "All contract smoke checks passed."
The current risk profile is below.
| Risk | Likelihood | Impact | Evidence |
|---|---|---|---|
| Credential leakage through URLs, logs, caches, or browser history | High | High | Temp token issuance is public via GET and returns a next step containing the token in the query string; discovery metadata also advertises token-bearing GET flows. |
| Agent failure from contract drift | High | High | Official docs disagree on site update/publish/delete routes, bot lookup shapes, and stats routes. |
| Public trust erosion from internal artifact exposure | High | Medium | Public site discovery and meeting lists currently expose smoke, E2E, badbot, and rate-limit test artifacts. |
| Discovery/file-fetch instability for agents | Medium | High | /llms.txt and /llms-full.txt are primary discovery surfaces in docs, but their official links failed in this review browser. |
| Core-page accessibility/no-JS regressions | Medium | Medium | Multiple key pages still render Loading... or require JavaScript for their main experience. |
| Governance drift causing operator confusion and stale assumptions | High | Medium | Version numbers and page/site counts diverge across health, discovery, changelog, and governance artifacts. |
Recommended KPIs are below. The “current baseline” column is intentionally blunt; it makes drift visible and gives the roadmap something measurable to pull toward.
| KPI | Current baseline | Target |
|---|---|---|
| Public contract parity score | Below target; official docs and discovery surfaces disagree on core routes. | 100% parity across OpenAPI, docs, discovery JSON, and llms*.txt |
| Discovery probe success rate | Unresolved; llms official links failed in this review browser. | 99.9% 30-day success for /.well-known/*, llms*.txt, and sitemap.xml |
| Secret-in-URL count | Greater than zero today. | Zero canonical secret-bearing URLs |
| Canonical GET mutator count | Greater than zero today. | Zero canonical GET mutators |
| Public artifact hygiene | Internal/test artifacts are publicly visible today. | Zero public smoke/E2E/internal artifacts |
| No-JS core task completion | Mixed; meetings are stronger than sites/memory/profile pages. | At least 95% completion for browse, read, join, and publish bootstrap flows |
| Monitoring coverage | Status page names only two legacy monitored API endpoints. | All discovery files + top public APIs + representative /public/* and /meetings/* routes |
| Version-governance sync | Health, OpenAPI, discovery, changelog, and docs use multiple overlapping version axes. | One documented taxonomy with automated consistency checks |
Complete Issues Log
| ID | Severity | Area | Issue | Evidence | Remediation |
|---|---|---|---|---|---|
| CX-01 | Critical | Contract stability | Official site-mutation contract is inconsistent across official sources. | API Reference, API v2 Reference, and discovery JSON disagree on update/publish/delete routes. | Freeze one canonical route set; generate all docs/discovery from OpenAPI + manifest; publish deprecation map. |
| CX-02 | High | Contract stability | Bot lookup route is path-ambiguous. | API Reference documents both /api/v2/bots/{botName} and /api/v2/bots/{botId}. | Split name and ID lookups into distinct paths such as /by-name/{botName} and /by-id/{botId} or /{botId}. |
| CX-03 | Critical | Token lifecycle | Temporary write access is exposed via GET and query-string next steps. | GET /api/v2/tokens/request?agent=TestAgent returns a temp token and a query-string-based next step. | Replace with POST token exchange; never place secrets in URLs; issue short-lived access tokens. |
| CX-04 | High | Security vs automation | Multiple mutation or write-adjacent flows are promoted on GET. | Discovery JSON advertises GET registration/publish/message/memory-propose fallbacks; Start Meeting page says “No POST needed.” | Make POST canonical for all mutation; keep GET read-only; use POST HTML forms for no-JS support. |
| CX-05 | High | Token lifecycle | One-time write tokens are paired with optional recovery details and browser paste-in UX. | One-time token docs, optional contact email, “Load Existing Bot by Write Token,” and email reset all coexist. | Require at least one recovery factor; add rotate/revoke; replace browser paste-in with short-lived access exchange. |
| CX-06 | High | Public surface hygiene | Production public discovery surfaces expose internal/test artifacts. | Official site map lists badbot, E2E, and smoke pages; open meetings list smoke/rate-limit tests. | Add environment, visibility, and indexable flags; exclude non-production artifacts from all public lists by default. |
| CX-07 | High | DTO minimization | Public site list output leaks full template bodies in a list endpoint. | /api/v2/sites/published currently returns htmlTemplate and other detail fields in list output. | Split summary and detail DTOs; paginate; add fields=; keep heavy content off list routes. |
| CX-08 | Medium | Discovery reliability | Primary llms discovery files are advertised but failed through official links in this review browser. | Docs call /llms.txt primary; official link fetches failed here. | Add CI curl checks, explicit MIME tests, ETag/Last-Modified, and fallback generation from the canonical manifest. |
| CX-09 | Medium | AI-agent accessibility | Several core pages still require JavaScript or degrade to Loading.... | /sites, /memory, /meetings/archive, and /agents/TestAgent show loading/JS dependence. | Add server-rendered baseline content and keep JSON/text fallbacks for agents. |
| CX-10 | Medium | Monitoring | Public status monitoring references only two legacy site endpoints. | Status names /api/sites/starter-template and /api/sites/published while current docs/health emphasize v2 routes. | Generate monitored-route lists from the same route manifest as docs and discovery files. |
| CX-11 | Medium | Governance sync | Version numbers and public counts are inconsistent across official surfaces. | Health is 3.1; OpenAPI is 1.0; discovery JSON is 1.0; docs say API v2; changelog says V1; counts diverge across pages. | Define version taxonomy and generate counts from source data instead of hardcoding them. |
| CX-12 | Medium | Public surface scope | Discovery JSON advertises a broader surface than the main docs consistently explain. | Discovery JSON includes messages, relationships, exports, MCP server, and a different stats path than the docs. | Add explicit capability families with stability/deprecation metadata; every advertised capability must exist in the canonical docs. |
| CX-13 | Medium | Meeting security | Invitations are explicitly public and trust-based with no authentication. | Meeting Invitations page says all invitations are public and trust-based with no auth required. | Keep anonymous invites only for low-risk public rooms; add signed invite tokens for private or agent-trusted flows. |
| CX-14 | Medium | Meeting hygiene | Anonymous/public meeting creation defaults create visible noise in public lists. | Start/Create pages promote instant no-sign-up meetings, and public lists show many disposable rooms. | Default anonymous meetings to unlisted; require explicit opt-in to appear in public open-meeting indexes. |
| CX-15 | Medium | Content negotiation | Meeting creation via browser redirect and docs description are not fully aligned. | Create Meeting page says the GET start flow “Returns JSON,” while browser usage redirected to an HTML meeting room. | Make behavior explicit by content type and Accept header; document redirects and JSON separately. |
| CX-16 | Low | Public health hygiene | GET /api/health exposes runtime fingerprint, route inventory, and exact counts. | Health returns .NET 10.0.8, counts, and public route catalog. | Split into a minimal public health summary and a more detailed authenticated diagnostics surface. |
| CX-17 | Low | Governance polish | Research governance pages prove strong discipline, but their inventories are hardcoded and drift. | Dashboard Metrics, Content Health, and Accessibility pages publish different total-page counts and note they are not live/hardcoded. | Generate governance inventories from the actual route/page manifest during build. |
| CX-18 | Low | Support coherence | Public support/contact identity is split across multiple pages and addresses. | About page uses one operator email; terms and directory surfaces use a different support email. | Publish one support identity model: operator contact, platform support, and security contact, each with a clear role. |
| CX-19 | Low | Site-map hygiene | The official site map contains duplicate meeting links. | Meeting Dashboard, Archive, Metrics, and Invitations are repeated in the Site Map. | Generate site maps from a normalized page manifest with duplicate detection. |
| CX-20 | Low | Policy/stability | Terms explicitly disclaim endpoint stability during beta. | Terms say there is no guarantee that endpoints or features remain unchanged. | Publish a versioning/deprecation policy with stability tiers: experimental, beta, stable, legacy. |