Skip to content

July updates - #369

Merged
QSchlegel merged 136 commits into
mainfrom
preprod
Aug 14, 2026
Merged

July updates#369
QSchlegel merged 136 commits into
mainfrom
preprod

Conversation

@Andre-Diamond

Copy link
Copy Markdown
Collaborator

No description provided.

Andre-Diamond and others added 30 commits June 1, 2026 11:30
…d wallet integration

- Introduced Dockerfile for Playwright to set up the testing environment.
- Created docker-compose configuration for orchestrating services including PostgreSQL and the application.
- Added detailed instructions for running Playwright tests locally in RUNNING_LOCALLY.md.
- Implemented authentication fixtures to handle wallet session management during tests.
- Developed wallet fixture for CIP-0030 wallet injection and interaction with the application.
- Added global setup for environment variable validation and context loading.
- Implemented helper functions for signing transactions and fetching UTxOs from Blockfrost.
- Created a comprehensive ring transfer test suite to validate multi-signature transactions.
- Added configuration for Playwright tests including output directories and reporting.
fix: instantiate provider lazily in resolveAdaHandle to prevent crashes in preprod environments

feat: persist hasCheckedSession in sessionStorage to avoid redundant session checks and modal displays

refactor: make NEXT_PUBLIC_BLOCKFROST_API_KEY_MAINNET optional for better environment handling

fix: ensure BlockfrostProvider is instantiated only with a valid API key
- Updated walletFixture to improve error handling and state management during wallet interactions.
- Modified meshSign to correctly handle transaction signing with CIP-30 compliance.
- Enhanced ring-transfer tests to include better diagnostics and ensure spendable UTxOs are available before transactions.
- Improved new transaction page to handle insufficient funds gracefully.
- Updated UTxO selector to reset state on fetch failure.
- Added early return in sendDiscordMessage for empty recipient lists.
- Configured default network setting in Zustand store based on environment variable.
- Introduced stored payment script wallet handling in wallet building logic.
- Excluded additional directories from TypeScript compilation.
- Added build arguments for Blockfrost API keys in docker-compose files to ensure consistent client bundle paths.
- Updated Dockerfile to handle Blockfrost API keys as environment variables for production builds.
- Modified CI workflow to allow insecure cookies for wallet sessions during testing.
- Refactored wallet handling in various components to use the new useMeshWallet hook, ensuring compatibility with the latest wallet API.
- Enhanced error handling in wallet session management to prevent issues with unconnected wallets.
- Add notification center for handling signature required notifications.
- Create email channel for sending notifications via Resend.
- Define types for email messages and send results.
- Implement event types and notification statuses for email notifications.
- Create outbox for managing notification deliveries.
- Resolve signature recipients based on notification settings.
- Develop email templates for signature required and email verification notifications.
- Implement worker to drain notification outbox and send emails.
- Create API endpoints for draining notifications and verifying email addresses.
- Add TRPC router for managing wallet signer notification settings and sending reminders.
…cess

- Update wallet notification settings to utilize connected wallet address for managing notifications.
- Improve email verification flow with enhanced error handling and user feedback.
- Introduce new utility functions for address normalization and authorization checks.
- Refactor notification API to support signer address in queries and mutations.
- Update environment configuration for notification link base URL.
- Updated @playwright/test from ^1.50.0 to 1.60.0 in package.json and package-lock.json.
- Removed unnecessary comments and improved code readability in resolve-adahandle.tsx.
- Simplified session checking logic in layout.tsx by removing sessionStorage handling.
- Cleaned up data-testid attributes in RecipientRow.tsx and index.tsx.
- Removed redundant state management in UTxOSelector.tsx.
- Removed unused broadcastDone state in transaction-card.tsx.
- Enforced required Blockfrost API key in env.js.
- Refactored wallet session cookie handling in walletSession.ts.
- Removed unused function buildStoredPaymentScriptWallet in common.ts.
- Simplified getProvider function to directly return BlockfrostProvider instance.
Add Playwright end-to-end testing setup with email notifications
…uthorize"

createUser is a protectedProcedure (it needs a wallet session), but the
onboarding effects call it on *connect* — seconds before the user approves the
signing prompt. That first attempt always runs unauthenticated and is rejected
UNAUTHORIZED, and it never retries once the session cookie is set. The result:
useUser() stays null and the connect button is permanently stuck on "Authorize"
even though signing succeeded. Returning users with a live 7-day session cookie
were unaffected, which is why it slipped through the Mesh 2.0 / preprod merge.

Re-run createUser in handleAuthModalAuthorized — after the wallet-session cookie
exists — for both regular browser wallets (stake/DRep from the 1.9 IWallet) and
the UTXOS smart wallet (stake/DRep from its CIP-30 interface).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(auth): create User after authorization so login isn't stuck on "Authorize"
- Moved metadata fetching and normalization logic to a dedicated module.
- Introduced `fetchProposalMetadataWithFallback` to streamline metadata retrieval with fallback options.
- Simplified proposal metadata normalization and fallback creation.
- Added tests for proposal metadata helpers to ensure functionality and reliability.
- Removed redundant code and improved overall code organization for better maintainability.
Landing: new "Connect your AI agent" section at the top of the homepage with the downloadable skill, an animated typing prompt, and copy that makes clear the agent reads/drafts but never signs — keys and signatures stay with the co-signers.

Blog: markdown-based SEO blog at /blog with two starter posts (AI-agent skill, Cardano multisig basics). Per-post <head> is server-rendered (title, canonical, og:type=article, Article JSON-LD) by returning a resolved seo via pageProps, which _app renders outside the ssr:false boundary; posts are listed in the sitemap with lastmod.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: AI-agent landing section + SEO blog
QSchlegel and others added 19 commits August 13, 2026 00:43
Three governance tools, answering the three questions a treasury actually
asks:

  governance_list_ballots    the team's internal decision log — how the
                             signers decided on each proposal, with any
                             drafted rationale
  governance_vote_history    what the wallet's DRep actually put on-chain,
                             newest first, with proposal titles
  governance_open_proposals  active proposals this wallet has NOT voted on
                             — the outstanding decisions. Cross-references
                             live proposals against on-chain vote history;
                             includeVoted returns the whole active set
                             annotated with how we voted.

open_proposals degrades rather than fails: if the DRep is unset or Koios is
unreachable it still returns the active set and flags
voteHistoryUnavailable, so a model can say "I could not check" instead of
implying nothing has been voted on.

Two handler changes to support them, both mirroring the pattern already
used for botBallotsUpsert:

- botBallots gains a human branch. It was bot-only and required
  ballot:write even to GET; a wallet signer can already read these ballots
  through the tRPC router, so the bot gate now sits behind isBotJwt and
  humans are authorized by the same signer-or-owner predicate.

- drepInfo gains a wallet-access check. It previously looked up ANY
  walletId for ANY authenticated caller, letting anyone map a wallet id to
  its DRep credential. Now goes through authorizeProxyReadForV1, the
  canonical dual-identity read check. This is a pre-existing hole, fixed
  here because this change exposes the endpoint through MCP.

Landing page: the hero now leads with MCP rather than one vendor. It shows
the endpoint and a plain `mcpServers` JSON block that works in any client,
with a per-vendor CLI mentioned only as an aside. Prompts in the demo were
rewritten to match tools we actually expose, including the governance
ones. The setup card is vendor-neutral for the same reason and gained an
anchor the hero links to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(mcp): governance tools, and a vendor-neutral setup guide
MCP governance tools + vendor-neutral setup guide
The top hero had no MCP entry point at all — connecting an agent was only
reachable by scrolling to the developer section. It now sits alongside
Connect Wallet and Explore features, in both the signed-in and signed-out
states, and it is the one thing a visitor can set up before owning a
wallet.

Also renames the card's primary button from "Setup guide" to "MCP setup
guide", so the label says what it links to.

The page scrolls inside `main` rather than the window, so the fragment
link was worth checking rather than assuming: clicking it moves 5,100px
and lands #connect-mcp at the top of the viewport.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add a "Connect via MCP" button to the hero
…endpoint

Agents can now draft a rationale and publish it; submitting the vote and
signing remain with the wallet's signers.

**Security fix.** POST /api/pinata-storage/put had no authentication at
all — anyone on the internet could pin arbitrary content to the project's
Pinata account, burn its quota, and leave no record of who did it. It now
requires the app's wallet session or a v1 bearer token. Both existing
callers (registerDrep, updateDrep) are same-origin in-app fetches that
already send the session cookie, so they are unaffected.

**New endpoint.** POST /api/v1/ballotRationaleAnchor turns the text already
stored in a ballot's rationaleComments into a CIP-100/136 JSON-LD
document, pins it, and writes the anchor URL and hash back at the same
proposal index. Anchors were previously only settable by hand — pasted in,
or round-tripped through the ballot CSV. Authorization matches
botBallotsUpsert: bots need ballot:write plus wallet access, humans are
gated on the shared signer-or-owner check.

**The hashing is the subtle part.** hashDrepAnchor is not a JSON-LD
canonicalisation — it is blake2b-256 over JSON.stringify(doc, null, 2).
Verified empirically: reordering keys changes the digest, and the digest
matches the two-space pretty form, not the minified one. So the bytes
pinned must be exactly that form or a verifier fetching the URL and
re-hashing gets a different answer than the chain records.
serializeRationale is the single place that decides those bytes, and both
the pin and the hash read from it. Checked end to end against the real
hashDrepAnchor: hash === blake2b256(pinned bytes).

Empty optional fields are omitted rather than emitted as empty strings,
since every key changes the hash.

Extracts the Pinata call into src/lib/server/pinataUpload.ts so the new
endpoint pins directly instead of looping back through an HTTP route.

The MCP tool `ballot_publish_rationale` (scope ballots:write) is the
second and last write tool. mcpTools.test.ts now asserts exactly two
writable tools, both non-destructive — widening that list stays a
deliberate act.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Publish ballot rationales to IPFS (and close the open pin endpoint)
…ed tool list

Three connected changes: users can now see what the tools are, control what
each client may do, and audit what it actually did.

**Permissions are now enforceable, not just displayed.** The MCP endpoint
previously trusted the access token's `scope` claim. Those tokens are
self-contained and live an hour, so removing a permission — or revoking a
connection outright — did nothing until the token happened to expire, which
makes a permissions UI a lie. `resolveMcpCaller` now reads the stored
OAuthGrant on every request: a missing grant is a 401, and the token's
scopes are *intersected* with the grant's, so a change applies on the very
next call and a token can never gain reach it was not issued with. Costs
one indexed lookup, on the same path that already does one for bots.

The profile card gains per-scope checkboxes and a save action.
`updateConnectionScopes` moves the grant and its live refresh tokens in one
transaction, so a refresh cannot re-widen what was just narrowed. Emptying
a grant is refused: a connection that authenticates but can do nothing
reads as broken, and revoking is the honest action.

**Tool calls are now recorded.** Every invocation writes an AuditLog row
from the single wrapper in `createMcpServer` — tool, client, scope, status,
duration — keyed to the wallet it touched, so the per-wallet view is an
indexed lookup rather than a scan. Only the walletId is read from the
arguments: tool inputs carry user-authored prose (rationales, descriptions)
that has no place in an audit row. Writes are fire-and-forget; an audit miss
must never break a tool call.

**Wallet overview gains an AI client activity card**, listing the clients
that used *this* wallet with call counts and failures, expandable to the
individual calls. The profile answers "what have I connected"; this answers
"what has touched this wallet", which is the question a co-signer asks.

**The landing page lists the tools**, grouped by the permission each needs.
The list lives in `src/data/mcp-tools.ts` rather than importing the registry,
which would drag the API handlers and Mesh WASM into the client bundle; a
test asserts the two match name-for-name and scope-for-scope. A second test
pins the audit action string the tRPC router hard-codes for the same reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(mcp): permissions management, per-wallet activity, and a published tool list
…iners

Three separate overflows on the landing page, all invisible in the built
output because an ancestor clipped them instead of scrolling.

The MCP card was the worst: a grid item defaults to min-width:auto, so the
column refused to shrink below the min-content width of the JSON config
block (482px). On a 375px phone that forced the single column to 395px
inside a 261px card, and the wrapper's overflow-hidden clipped the heading
and body copy clean off — no scrollbar, just missing text. min-w-0 down the
chain lets the <pre>, which already had overflow-x-auto, scroll on its own.

The two button rows overflowed sideways because a third CTA was added to a
nowrap row. The hero's three buttons need 747px in a 673px container below
~780px; the MCP row turns horizontal at sm but its grid column halves at md,
so between those breakpoints its buttons ran into the panel alongside.
Wrapping self-corrects at every width. The hero also aligns to the top now:
its first CTA carries a subtitle, so centring floated the other two.

Verified by measuring container spill at 375 / 640 / 768 / 900 / 1024 /
1280: previously 118px (MCP) and 74px (hero), now zero at every width, with
both rows still on a single line at desktop. A sweep of the fully mounted
page confirms no other element loses content to a clipping ancestor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(homepage): stop the CTA rows and MCP card overflowing their containers
…grant

The two ballot tools could not be granted by anyone. Clients request exactly
the `scope` from the 401 challenge rather than the scopes_supported
catalogue, and the challenge named only the two read scopes — so no grant
ever covered ballots:write, however the user connected. The comment above
CHALLENGE_SCOPES described this failure mode precisely and then omitted the
scope anyway, reasoning it should stay "opt-in for a client that asks for it
explicitly". No client asks; they follow the challenge.

Withholding a scope was never the server's call. The challenge now
advertises all three and the consent screen decides, one checkbox per scope,
pre-ticked with what the client asked for. /api/oauth/decision intersects
the posted selection with the signed request handle, so the body can only
narrow the grant, never widen it past what /authorize already validated. An
approval that grants nothing is refused rather than recorded: a zero-scope
token authenticates and exposes no tools, which reads to a client as a
broken server rather than a refusal.

A client that sends no scope at all still defaults to wallets:read only.

Two scope descriptions were understating themselves, which matters more now
they are what the user reads at the moment of deciding: governance:read also
exposes the team's internal ballots and DRep vote history, and ballots:write
publishes rationale documents publicly and near-permanently to IPFS.

Adds oauthDecision.test.ts — the endpoint had no tests, and it is the point
where consent becomes a durable grant. Mutation-checked: reverting the
narrowing logic fails 4 of the 8. Consent screen verified rendering against
a production build: three checkboxes, correct copy, toggle working, and the
empty-selection guard confirmed live independently of the session gate.
fix(mcp): make every scope reachable, and let the user pick which to grant
…rnance activity

- Added new API endpoint `/api/governance/txGovernance` to fetch governance activities (votes and DRep certificates) per transaction using Koios `tx_info`.
- Introduced helper functions in `koios.ts` for making GET and POST requests to Koios API.
- Implemented logic to filter and normalize governance certificates and votes.
- Joined proposal titles to votes for better context in the response.
- Added rate limiting and input validation for the new endpoint.
- Created a new hook `useTxGovernanceBadges` to fetch governance badges based on transaction hashes.
- Added tests for the new API endpoint to ensure correct behavior and error handling.
- Updated existing types and utility functions to support the new governance features.
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
multisig Ready Ready Preview Aug 14, 2026 7:30am

Request Review

@Andre-Diamond Andre-Diamond changed the title fix: ensure serializedScript function handles missing scriptCbor correctly Jun-August updates Aug 14, 2026
Comment thread src/lib/oauth/clients.ts Dismissed
Comment thread src/lib/tx-draft/mutations.ts Fixed
@Andre-Diamond Andre-Diamond changed the title Jun-August updates July updates Aug 14, 2026
@railway-app
railway-app Bot temporarily deployed to victorious-warmth / preprod August 14, 2026 07:26 Inactive
@QSchlegel
QSchlegel merged commit a4dd582 into main Aug 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants