feat(http): asterisk cover for the token, drop Shown once - #60
Merged
Merged
Conversation
Keep a hover/focus asterisk mask instead of blur, and remove the Shown once callout so the page is just the value and how to copy it.
kapustazh
added a commit
that referenced
this pull request
Jul 26, 2026
* setup typescript project - add package setup and scripts - add strict typescript config - add empty app entry point * add mcp server dependencies - add the official mcp typescript sdk - add zod for tool schemas - lock dependency versions * create mcp server factory - add the deeptrace server identity - keep server setup reusable for transports and tests * setup code quality checks - add type aware eslint rules - add prettier formatting - add lint and format scripts * code quality github-workflow - install dependencies from the lockfile - check formatting lint types and build - run on pushes and pull requests * chore: document environment variables and Nuthatch deployment * feat: define source adapter contract, registry types and fixtures * add unit test infrastructure (#5) - add Vitest scripts and MCP server factory coverage - share TypeScript test configuration with source fixtures - run unit tests in the code quality workflow * Add MCP stdio lifecycle (#6) * add MCP stdio lifecycle - connect the server through a reusable stdio runtime - handle clean idempotent signal shutdown - test initialization and lifecycle behavior - document local build and client setup * clarify Nuthatch deployment documentation * feat(m2.2): enumerate Base DEX candidates * Add source adapter runtime validation (#7) * add source adapter runtime validation - mirror the canonical source interface with strict Zod validators - enforce source-specific success and failure invariants - validate existing fixtures and malformed boundary cases * co-locate source schemas and types - make the canonical source schema the runtime and type source of truth - remove the duplicate runtime schema module - use concrete schema-derived result types for fixtures * feat(m2): add redacted probe transport * feat(m2): add liveness sweep * feat(m2): add candidate data probes * feat(m2.7): assert Graph deployment identity * Add shared gateway primitives (#10) * add shared gateway primitives - add typed configuration and rate-limit errors - add validated gateway defaults and environment overrides - add injected-clock fixed-window limiting with unit coverage * address gateway review findings - prune expired rate-limit keys on cleanup boundaries - keep rate-limit error text fixed and secret-safe - cover pre-boundary rejection and expired-key eviction * test(m2.3): capture primary liveness * test(m2.3): capture alternate liveness * test(m2.4): classify primary Uniswap schemas * test(m2.4): classify alternate Uniswap schema * test(m2): capture blocked source evaluation * test(m2): keep minimal source evidence * Define M0 core policy (#12) * define M0 core policy * document M0 core policy * clarify M0 policy ownership * Add compare_pools response contracts (#15) * add compare pools response schemas * test compare pools response contracts * tighten response contract invariants * align source availability invariants * validate failed source coverage * add canonical pool normalization primitives - normalize chain-aware token, pair, and pool identities - preserve source-reported decimal strings, zero, and null values - collapse duplicate pools deterministically with focused tests * M2: close source scope to two Tier-B Graph deployments (#17) * feat(m2): finalize registry records for the two validated sources Every identity value is traced to committed M2 evidence, not to the summary prose: - deployment IDs read from each source's 01-meta.json `_meta.deployment`; - subgraph IDs from manifest.json `validated_selections`; - supported_entities is the Tier-B set from the M2 plan; - schema_version/methodology_version stay null because Tier-B deployments publish neither. source_id reuses the manifest slug so each record joins back to its evidence directory by name. Verified through the M3.1 loader: both records resolve via getSourceById with the expected protocol tags. * docs(m2): amend the source scope to two deployments Records the owner decision to ship MVP-0 with two same-tier Graph sources rather than three. The original blocked status is retained verbatim below the amendment so the reduction reads as a deliberate scope change, not a gate that was quietly satisfied. Notes that M3's exit criterion becomes two SourceResult values, and that a third source stays a configuration-only addition. PLAN.md and the M2 tracker boxes are deliberately untouched: those are M2.9 owner items behind the knowledge-check gate. * test(m2.8): replace source fixtures with live M2 values * docs(m2.9): lock MVP-0 scope to two Tier-B Graph sources * chore: add opencode executor reviewer agents * chore: register grok code reviewer agents * chore(m2.8): format source fixtures * chore: remove duplicate reviewer agent * chore: switch opencode agents to glm-5.2 * chore: switch opencode config to glm-5.2 --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * fix m2 probe harness review findings (#19) - prefer GRAPH_API_KEY from process env without requiring .env - preserve transport timeout and error messages in pool probes - move harness tests into the vitest quality gate and typecheck scripts/m2 * M3: Graph adapter — registry loader and daily aggregation (#18) * feat(m3.1): load typed source registry records Validate records.json at load time with a strict runtime schema instead of asserting it into SourceRegistryRecord[]. Repository configuration is an untrusted deploy input, so every failure names the offending JSON path. The gateway host allowlist, Base chain id, and locator shape are enforced by the schema, and records are deep-frozen before they reach callers. * feat(m3.1): validate compare-pools profiles Add the MVP request profile as registry-owned configuration rather than extending SourceRegistryRecord, which describes source identity and location and should not absorb one milestone's pool, query, and ordering choices. getActiveComparePoolGraphSources joins each binding to an active Graph record and enforces the invariants the adapter would otherwise have to trust: exactly three bindings, unique source, pool, and priority, a shared query and response contract across the set, and entity coverage for the selected query. Results come back frozen and in ascending priority order so output order never depends on response timing. * test(m3.1): cover invalid registry records Prove that a bad records.json fails loudly with a path-specific diagnostic: missing file, malformed JSON, wrong container shape, unknown keys, a gateway host outside the allowlist, a non-Base chain, empty subgraph and deployment pins, a locator that contradicts its source type, and duplicate source ids. One error reports every issue found so a bad deploy is fixed in one pass. * test(m3.1): cover invalid compare-pools profiles Prove each load-time invariant rejects rather than degrades: non-three cardinality, duplicate source, pool, or priority, unknown, inactive, and non-Graph source references, a query or response contract that differs across the set, a mixed schema tier, checksummed and truncated pool addresses, a self-paired token, a non-Base chain, a non-positive priority, unknown keys, and a record missing an entity the selected query needs. Silently querying two sources, or two sources under different templates, would produce a comparison the contract cannot express, so these are startup failures rather than per-source warnings. * feat(m3.6): add exact decimal summation * feat(m3.1): bind two M2 pool sources * chore(m3.6): scope vitest exclude to node:test helper * chore(m3.1): format registry loader tests * feat(m3.6): aggregate completed UTC snapshots * test(m3.1): smoke test shipped registry profile * test(m3.6): cover unavailable daily history * fix(m3.1): use record chain_id in profile smoke * fix(m3.6): scope 7d consecutivity to the window * fix m2 probe harness review findings (#19) - prefer GRAPH_API_KEY from process env without requiring .env - preserve transport timeout and error messages in pool probes - move harness tests into the vitest quality gate and typecheck scripts/m2 * remove .opencode agent config - drop OpenCode executor/reviewer agents added with the M2 scope close - keep the DeepTrace workflow defined in AGENTS.md * fix graph adapter review findings - null or malformed days null the affected 7d aggregate instead of under-summing - align compare-pools query and schema ids with m2-tier-b-metrics-v1 - cover partial-null and malformed holes inside a consecutive 7d window * M4: Nuthatch contract probe and freshness-view draft (#21) * add nuthatch contract probe - capture the verified Nuthatch CLI and partial HTTP contract without overstating live view support - add fail-closed HTTP acceptance checks and quality-gate coverage - define the deterministic freshness-view draft with the locked 0.3% pool metadata * tighten nuthatch probe acceptance - require POST SQL rejection before the probe exits successfully - pin guard queries to the raw swap table in executable tests - keep retained evidence acceptance flags explicit * document nuthatch POST rejection gate - align the operator guide with the executable read-only acceptance checks --------- Co-authored-by: kapustazh <51422901+kapustazh@users.noreply.github.com> * Graph: live PoolSourceResult adapter for locked compare-pools sources (#22) * Add Graph live adapter emitting PoolSourceResult for locked compare-pools sources. * Clamp Graph adapter timeouts and cover HTTP/transport/token failure paths. * bind compare_pools request schema to locked two-source Graph scope (#23) - set expectedGraphResults to 2 and reserve unverified Nuthatch slot - add strict Base WETH/USDC request schema with policy defaults - add live-derived partial/failed/stale fixtures without inventing Nuthatch * bind Graph pool normalization to locked compare_pools allowlist (#24) - reject out-of-scope source, deployment, pool, pair, and chain before metrics - convert only allowlisted live Graph results via the pure M0-03A path - cover timeout passthrough and live fixture identity parity * M0-04: deterministic pool metrics ranking and Top-N (#25) * add deterministic compare_pools metric ranking and Top-N - lock source-reported USD methodology without APR/APY labeling - rank by exact decimal strings with nulls-last and policy tie-breaks - dedupe before Top-N and cover live Graph fixture order stability * reuse policy ranking tie-break constants in metrics methodology * M0-05: compare_pools coverage, freshness, and failure isolation (#26) * add deterministic compare_pools metric ranking and Top-N - lock source-reported USD methodology without APR/APY labeling - rank by exact decimal strings with nulls-last and policy tie-breaks - dedupe before Top-N and cover live Graph fixture order stability * reuse policy ranking tie-break constants in metrics methodology * add compare_pools quality settlement for coverage and freshness - settle independent Graph results into complete/partial/failed envelopes - derive quality freshness from lag without rewriting adapter status - keep Nuthatch explicit or unavailable without inventing a live fact * warn on Top-N truncation during compare_pools quality settlement * M0-06: compare_pools MCP tool (#27) * add compare_pools MCP tool with injected source gateway - register one read-only compare_pools tool behind rate limits and allowlisting - wire fixture/live gateways through normalize, rank, and quality settlement - separate request vs response validation errors and honor gateway source timeouts * reject unknown compare_pools fields at the MCP boundary - register a strict Zod input schema instead of a raw shape that strips extras - cover unknown-field rejection before source adapters run * M4: Nuthatch nest — P0 contract, P5 view, P6 checks, P4 evidence (#28) * docs(m4.0): reconcile the CLI surface with the plan * feat(m4.0): add nuthatch contract probe harness * feat(m4.0): probe nuthatch http surface * feat(m4.0): capture live nuthatch http evidence * feat(m4.6): return the latest swap deterministically * docs(m4.6): describe the freshness view * chore(m4.0): format http capabilities evidence * test(m4.8): add nuthatch view invariant checks * test(m4.8): record expected check fixtures * test(m4.5): capture raw row seam evidence * ci(m4.8): run nuthatch check from clean checkout * fix(m4.8): correct nuthatch download url * fix(m4.8): validate nest structure in CI * feat(m4.2): import nest config metadata * feat(m4.2): vendor pool swap abi * feat(m4.2): import generated nest schema * fix(m4.0): update http probe test for live evidence --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * remove ai_reasoning from compare_pools response contract (#29) - drop schema, settlement stub, fixtures, and reasoning policy budgets - keep structured MCP output only; client SKILL presents later - align PLAN.md with no internal AI decision * M5: Nuthatch adapter — client, response parsers, adapter (#30) * feat(m5.1): add nuthatch freshness query constant * feat(m5.1): add bounded nuthatch http client * feat(m5.1): add strict nuthatch response parsers * feat(m5.3): map nuthatch responses to source results * test(m5.1): cover nuthatch client boundary * test(m5.3): cover adapter ok readiness stale paths * test(m5.3): cover adapter failure matrix * feat(m5.4): register nuthatch source record --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * M0-07: MCP HTTP transport, registry build fix, testing guide (#31) * fix(build): copy registry json into dist * feat(http): serve mcp over streamable http transport * test(http): cover transport config parsing * docs(http): add mcp server testing guide * docs(http): add collaborator quickstart section * fix(http): dispose sessions that fail to initialize * test(http): cover failed initialize disposal * fix(http): release session reservation when open fails * chore: redact tailnet hostname from committed files * chore: redact tailnet hostname from nest semantics --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * fix(http): drop the browser credential challenge (#33) Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * fix: the 7d window has never returned a value (#34) * fix(m3): fetch eight day snapshots for the 7d window * test(m3): cover 7d sums with a partial newest day --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * M0-08: one-command MCP smoke test (#32) * feat(smoke): add one-command mcp smoke test * docs(smoke): document the mcp smoke script * chore: ignore the local handoff document * chore: ignore the local dev plan * fix(m0.8): recognize complete tool results --------- Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> * fix(http): share one rate limiter across sessions * feat(m5.6): invoke live Nuthatch freshness * fix(m5): keep degraded provenance honest * docs(m5): describe live Nuthatch source * fix(http): expire abandoned MCP sessions * test(http): cover idle session lifecycle * fix(http): expire abandoned SSE sessions * feat(m4.11): detect frozen Nuthatch backfills * fix(m4.11): configure RPC failover alerts * fix(m4.11): restore valid RPC fallbacks * fix(m3): version eight-day query provenance * add large swap search schemas - freeze the Base pool request and SwapEvent contracts - add complete, empty, paginated, failed, and duplicate fixtures - cover strict validation and Nuthatch freshness invariants * fix(m0.8): close smoke test sessions * fix(http): preserve MCP bearer challenges * test(smoke): lock accepted success statuses * fix(smoke): send negotiated protocol version * normalize large swap events (#43) - derive direction and exact human amounts from signed pool deltas - deduplicate canonical event identities and reject conflicts - filter selected-token thresholds without floating point or USD * feat(skill): guide verified pool research * feat(http): serve MCP at the root URL * docs: simplify public MCP connection * feat(mcp): teach clients verified pool research * fix(skill): align guidance with response schema * docs: add three-client MCP setup * feat(http): build public connection page * feat(http): route browsers to connection page * fix(http): validate MCP request origins * docs: explain MCP origin rejection * implement stable large swap pagination (#45) * docs: add one-command skill install (#46) Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com> Co-authored-by: Matvii Nesterenko <51422901+kapustazh@users.noreply.github.com> * Release find_large_swaps tool (#47) * add Nuthatch large-swap source - add allowlisted swap-search view and registry capability - validate receipts and scan bounded keyset batches - retain explicitly derived parity evidence * register find_large_swaps MCP tool - settle sole-source quality and bounded response pages - wire the live adapter into MCP and HTTP sessions - cover pagination, redaction, compatibility, and tool behavior * document large-swap search release - update plans, operator guidance, and agent skill - smoke-test both released MCP tools - preserve explicit Nuthatch deployment caveats * feat(http): add the public connection page assets The page ships no script, so client configurations use native disclosure elements and code blocks select on click rather than through a copy button. Its three subset typefaces are served from this origin so the document depends on nothing external. * feat(http): serve the connection page from a file The inline HTML constant becomes a file read, so the page is editable as a document instead of a template literal. font-src moves from 'none' to 'self' to admit the three same-origin typefaces. script-src stays 'none' and style-src still omits 'self', so styles remain inline. The typeface table is exact-match: request paths are only ever compared against its keys, so no caller-supplied string reaches the filesystem and traversal is not expressible. * feat(http): route typeface requests before the MCP paths The typefaces are the only non-MCP paths this origin answers, so they resolve ahead of the MCP path check and before authentication. The build now copies .html and .woff2 beside the emitted JavaScript, since tsc emits only JS and the runtime reads these at startup. Prettier skips the page because its <pre> whitespace is rendered output, and reformatting the inline stylesheet tripled the file. * test(http): cover the connection page typeface route Asserts the typefaces are served unauthenticated with immutable caching, that unknown and traversal-shaped asset paths are refused, and that the page links to nothing beyond its own fonts. * feat: compare Base markets through Messari standardized subgraphs (#48) * feat(registry): pin Messari standardized deployments on Base Replace the native Uniswap V3 and PancakeSwap records with one Messari dex-amm deployment bound twice, once per WETH/USDC fee tier, and add the three Messari lending deployments behind a new "lending" category. The compare-lending profile mirrors the compare-pools join, so both profiles share the loader's duplicate, category, and same-tier checks rather than growing a second set of validation rules. * feat(graph): read pool metrics from the Messari dex-amm standard Dispatch the pool adapter on query_id so the Tier-A Messari path and the native Tier-B path can coexist, and extract the parsing helpers both share into response.ts. Tier-A differs from the native schema in three ways that are easy to miss: inputTokens replaces token0/token1 and reports decimals as an Int, the fee tier lives in a fees array as a percentage rather than a bps integer, and snapshots are keyed by day-since-epoch rather than a midnight timestamp. The adapter converts the last one before handing snapshots to the shared completed-UTC-day aggregator, so window semantics stay identical across both tiers. The standard publishes no per-day fee field, so fees_usd maps to dailyTotalRevenueUSD. That is supply-side plus protocol-side revenue; on both compared pools the protocol-side share is zero today, but that is a property of the pools rather than a conversion performed here. * feat(lending): define the compare_lending_markets contract Lock the USDC market, the five ranking metrics, and the three-source coverage expectation in policy, then add the request and response schemas and the scope binding they validate against. The market token is policy rather than a request field: the tool compares one asset across every selected protocol, so letting callers vary it would change what the comparison means. Rates are carried as decimal strings with an explicit percent_apy basis so no layer is tempted to convert between APR and APY. * feat(lending): query the three Base lending deployments One query document serves Aave v3, Seamless, and Moonwell unchanged, which is the whole reason for standardizing: the sources differ by deployment, not by adapter. Two decisions worth naming. The adapter reads lendingProtocols.network back and rejects anything that does not self-report BASE, because a deployment published for one network can index another -- the Compound v3 "base" deployment reports MAINNET, which is why it is out of scope. Second, an absent rate pair stays null and silent because Moonwell genuinely publishes no stable borrow rate, while a duplicated or malformed pair yields null plus a warning, since both defaulting to zero and picking arbitrarily would invent a number no source reported. * feat(lending): rank and settle lending market results Rank by the requested metric with nulls last and deterministic protocol/market/source tie-breaks, then settle into the same status/coverage/freshness/provenance envelope compare_pools returns. Each protocol contributes at most one market for the locked asset, so unlike compare_pools there is nothing to deduplicate across sources. A market reporting isActive false is still ranked and returned with the flag and a warning, because its balances and rates remain real. * feat(mcp): register compare_lending_markets Wire the tool behind an injected source gateway and advertise it beside compare_pools with the same read-only annotations. The live lending gateway defers registry loading into fetchLendingResults rather than resolving it at construction, so creating the server cannot throw on a registry problem. Tests inject both gateways to stay offline. * test(smoke): exercise both tools in the MCP smoke check Require every public tool in tools/list and call each one, so a tool that is built but never registered cannot pass unnoticed. * docs: describe the Messari scope and the lending tool Record the pinned deployments, the field mapping, and the two caveats a reader will otherwise trip over: fees_usd is revenue rather than a fee field, and Compound v3 is excluded for self-reporting MAINNET. The superseded native Tier-B selection is kept at the end of source-scope so the earlier sweep's findings are not lost. Extend the existing skill to cover both tools rather than adding a second one, since an agent choosing between them needs a single set of rules. * fix(quality): count sources that answered, not records returned Both tools derived successful coverage from the post-Top-N record count, so asking for top_n 1 while all sources were healthy and fresh reported one successful source and settled partial. Top-N is the caller's choice, not a coverage gap, and the truncation already has its own warning. Coverage now counts the sources that returned ok. The response schemas relax from requiring equality to requiring that records never outnumber the sources that answered, which still catches the inconsistency the equality check was there to prevent. * feat(http): issue per-client bearer tokens One shared deployment token means a single leak compromises every client with no way to revoke selectively. The store issues per-client tokens and keeps only their SHA-256 digests, so the file is enough to revoke a token but never to use one. Verification is a digest lookup rather than a comparison, so its cost does not grow with the number of issued tokens and no timing-safe compare is needed: a digest cannot be walked back to the secret. isAuthorized accepts issued tokens and the shared token together, so existing clients keep working while the shared one is retired. * feat(http): let callers take their own token Access is open, so the endpoint is unauthenticated: a caller cannot present a token before something has given them one. The page carries its own policy because the connection page forbids forms outright, and this one needs exactly a single form. The per-address limit is cost control rather than access control. Anyone may hold a token; nobody may mint an unbounded number of them, since each one spends metered Graph quota. * docs(http): point the setup guide at self-serve tokens The page told visitors to request a token through a secure channel, which left no path for anyone who did not already know the maintainer. It now links to the issue endpoint. * docs(http): record where the issued token store lives Two properties of the deployment make the store path load-bearing and are easy to undo by accident: ProtectSystem=strict leaves the filesystem read-only, so the directory has to be one systemd made writable, and the deploy rsyncs /opt/deeptrace with --delete, so a store inside that tree would be discarded on every deploy. * test(http): cover the mint request a browser really sends The other cases post without an Origin header, so they never exercised the origin check that stands in front of the endpoint. That check also refuses a mint posted from another site, which is the CSRF case. * Add source-bounded wallet research (#51) * feat(wallet): define research contracts and cursor scope Freeze the Base-only request, section-aware response, registry capabilities, and wallet-bound fixed-snapshot cursor before source implementations depend on them. * feat(wallet): verify Aave wallet positions Bind wallet research to the proven Aave deployment and preserve exact position balances, snapshot valuation, freshness, and evidence without repricing. * feat(wallet): index bounded pool activity Add a dedicated sender/recipient Nuthatch capability with safe bounded scans, parity checks, source-local failures, and live Graph/Nuthatch composition. * feat(wallet): expose research_wallet through MCP Register the source-bounded wallet tool beside compare_pools, compare_lending_markets, and find_large_swaps so clients can request supported Base wallet facts without implying complete balances. * docs(wallet): define the supported research boundary Document the verified Graph scope, pool-bounded Nuthatch limitations, client workflow, and remaining live deployment gate so partial results cannot be mistaken for complete wallet history. * feat(http): add an OAuth browser flow for connecting Copying a token by hand is the friction. OAuth removes it: the client opens a page, the user approves, and the token returns to the client directly. The authorization server runs inside the resource server, which drops the parts that usually make OAuth expensive. There is no second host, no signing key and no JWKS, because a token can be looked up in the store rather than verified from a signature. PKCE with S256 is mandatory and callbacks must be loopback: a public client keeps no secret, so the redirect target is all that binds a code to its requester. An unrecognised callback renders an error rather than redirecting, since redirecting there is the open redirect it is meant to prevent. * feat(http): advertise the OAuth flow on a 401 Clients discover the browser flow from the challenge. Without resource_metadata the 401 is a dead end and the user copies a token by hand. * test(http): cover the OAuth flow end to end Follows one client from registration through consent to a token that authenticates MCP, then covers the failures that matter: a mismatched PKCE verifier, a replayed code, an unregistered callback, and a missing challenge. * fix(http): stop the origin allowlist blocking OAuth clients The allowlist ran ahead of routing, so any OAuth call carrying an Origin header was refused with invalid_origin. Clients set that header themselves — a CLI, an editor shell, a loopback callback — and those values cannot be enumerated, so the allowlist rejected working clients and nothing else. Discovery, registration and token exchange are now exempt; PKCE and single-use codes defend them. The MCP endpoint and the consent submission still require an allowed origin, which is where a cross-site POST is the actual threat. The earlier tests missed this because they sent no Origin header at all. * feat(http): make the shared deployment token optional (#54) DEEPTRACE_HTTP_TOKEN authenticates every client at once, so one leak exposes all of them and no single client can be revoked. Issued per-client tokens already stand on their own, which leaves the shared credential necessary only until pre-migration clients move over. Omitting the variable now retires it instead of failing startup, making retirement an operational step rather than a code change. A value that is present but too short is still rejected, since a weak shared secret is worse than none, and startup warns while the variable remains set. * fix(http): stop the token page rejecting its own form (#56) The page sent Referrer-Policy: no-referrer, which is the one policy that makes a browser set Origin to the literal string null. The origin allowlist that guards minting does not contain null, so every real click on Create a token answered 403 invalid_origin. Only curl succeeded, because a request with no Origin header at all is treated as same-origin. same-origin keeps the referrer off other sites while leaving the page's own form carrying a real origin, so the allowlist still refuses a cross-site post. Existing tests passed throughout: they send either the allowed origin or an attacker's, never the value a browser actually sends from this page. The added test pins the header and the null-origin rejection together, so reintroducing no-referrer fails in CI rather than in the browser. * Rewrite the README and add an authentication guide (#55) * docs(readme): rewrite for a first-time reader The opening was a single dense sentence that only parsed if you already knew the product, and the four tools were listed without saying what anyone would use them for. Lead with the wordmark and a plain claim, map real questions to tools, then explain each tool by what it refuses to do — no USD conversion in the swap search, one shared query template across three lending protocols, a frozen snapshot behind the cursor. Scope limits move from the source-scope docs onto the front page, and per-client setup covers Claude Code, OpenCode, Codex, and the mcp-remote bridge rather than one example. * docs(auth): add a guide for getting a credential Authentication was spread across the connection page, the setup guide, and the operator notes, so no single place explained that access is self-serve or that there are two ways in. Collect it into one page: the uniform key/value connection every harness accepts, the OAuth browser flow and its endpoints, minting a token directly for CI, and the operator notes on the token store and origin exemptions. Record the current client-compatibility limits rather than implying the flow works everywhere — only IPv4 loopback callbacks are accepted, only two discovery paths are answered, and the in-memory client registry does not survive a restart. The connection guide keeps per-client configuration and links here instead of repeating it. * Update README.md * docs(auth): correct how a script obtains a token The endpoint always answers in HTML and has no JSON representation, so the documented curl returned a page rather than a token. Show the scrape a script actually needs, and note that the per-address hourly limit answers 429 with a page containing no token — a scrape then yields an empty string instead of an error, which reads as an authentication failure later. * docs(readme): close the bold on the tagline The shortened tagline lost its closing markers, so the line rendered with a literal ** and left the rest of the document emphasised. * Blur the issued token and stop capping mints (#57) * feat(http): blur the issued token and stop capping mints The token was rendered in plain text, so it survived a screen share or a screenshot taken for any other reason. It is now blurred until pointed at or focused. The blur is presentation only: the value stays selectable underneath, so one click still selects the whole token and the page needs no script, which keeps its script-src 'none' policy intact. Minting is no longer capped per address. The cap could not limit access, since anyone refused could take a token from another address or simply wait, so in practice it only blocked someone reconnecting. Metered spend is bounded where it is actually incurred, by the request rate limiter in front of the tools. Removing it also retires the 429 page. * docs(auth): drop the mint cap and describe the blurred token Minting is no longer capped per address, so the 429 page and the advice to reuse a token rather than mint one no longer describe the server. Record that the issued token is blurred until pointed at, since a reader who does not know that will think the page failed to show it. * docs(readme): show one Cursor and Codex example instead of every client (#58) * docs(readme): show one Cursor and Codex example instead of every client The Connect section carried a full config block for each client plus the mcp-remote bridge, which buried the fact that all of them need the same URL and header. Keep Cursor and Codex as representative examples and leave the exhaustive per-client matrix to docs/connect.md. * docs(readme): use the mcp-remote bridge form for the Claude example Swap the Cursor example for Claude and show it the way The Graph documents its own server: the mcp-remote command form, which works in both the desktop app and Claude Code. Move the shell export next to Codex, the only one of the two that reads the token from the environment. * feat(http): cover the issued token with asterisks instead of blurring it (#59) A blur still shows the shape of the value and reads as a rendering glitch rather than a deliberate cover. Swap two spans instead: only one is ever in the layout, so the asterisks can never be caught in a copy and the value stays out of reach until hover or focus asks for it. Still no script, so the page keeps its own CSP. * feat(http): cover the token with asterisks and drop Shown once (#60) Keep a hover/focus asterisk mask instead of blur, and remove the Shown once callout so the page is just the value and how to copy it. * fix(nuthatch): query pool__swap without CAST for wallet and LSS (#61) * fix(nuthatch): query pool__swap without CAST for wallet and LSS Authored views and CAST(... AS VARCHAR) caused /sql HTTP 400s while freshness on pool__swap still worked. Align live scans with the indexed table, parallelize head SQL with nest/schema like compare_pools, and surface Catalog Error detail in warnings. * fix lint in wallet activity concurrency test * docs(readme): note that Nuthatch is reachable only from the server The Connect section said a normal user does not need "Nuthatch access", which reads as an optional extra rather than a boundary. Nuthatch is a private, unauthenticated loopback service inside the DeepTrace server with no public route, so there is nothing a client could connect to even if it wanted one. --------- Co-authored-by: ikodo0 <nxorxxx@gmail.com> Co-authored-by: ikodo0 <ikodo0@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
vitest run tests/unit/token-issue.test.ts/auth— no Shown once box, asterisks (not blur) until hover, copy still works