From 9bfe994daf6e5e06d28b9cd8c5f597c49ecdb53b Mon Sep 17 00:00:00 2001 From: Divyansh Date: Fri, 19 Jun 2026 18:13:04 +0530 Subject: [PATCH 1/3] chore: restore BUGS.md file --- BUGS.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 BUGS.md diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 0000000..ac96702 --- /dev/null +++ b/BUGS.md @@ -0,0 +1,73 @@ +# BUGS.md — Terminal 3 SDK / docs friction log + +Parallel bug-bounty track. Every claim-flow / SDK / docs friction encountered +while building gets logged here (severity, repro, expected vs actual). + +> TODO: email `devrel@terminal3.io` to confirm where bug submissions should go. + +--- + +## #1 — Spec says "API key", SDK has no API-key concept + +- **Severity:** Low (docs / onboarding friction) +- **Where:** Onboarding, `@terminal3/t3n-sdk@3.5.2` +- **Repro:** Following an "agent" mental model, you look for `T3N_API_KEY` to + authenticate the client. There is no such parameter. +- **Expected:** A documented API-key or token to identify the agent. +- **Actual:** The agent authenticates with an **Ethereum private key** (or OIDC). + The DID is *derived* from the key via `eth_get_address` → + `createEthAuthInput` → `client.authenticate`. There is no API key. +- **Resolution in this repo:** env var is `T3N_AGENT_PRIVATE_KEY`; the secret + never leaves the server and only the public DID/address is exposed. + +## #2 — No single end-to-end quickstart (API key -> first resolved DID) + +- **Severity:** Medium (docs gap, known) +- **Where:** `https://docs.terminal3.io` + package README +- **Repro:** As a newcomer, there is no one page that walks from + "I have credentials" to "I authenticated and resolved my first DID". +- **Expected:** A copy-paste quickstart that ends in a printed DID. +- **Actual:** You assemble it from the README snippets: + `setEnvironment` → `loadWasmComponent` → `eth_get_address` → + `new T3nClient({ handlers: { EthSign: metamask_sign(...) } })` → + `handshake()` → `authenticate(createEthAuthInput(address))` → `getDid()`. + Verified working against `testnet` (node `cn-api.sg.testnet.t3n.terminal3.io`). + +## #3 — `handshake()` returns `did: null` / `authenticated: false` + +- **Severity:** Low (mild surprise, not a blocker) +- **Where:** `T3nClient.handshake()` return value +- **Repro:** `const hr = await client.handshake()` → + `{ sessionId, expiry: 0, authenticated: false, did: null }`. +- **Expected (naively):** handshake yields the identity. +- **Actual:** Identity only appears after the separate `authenticate()` call. + This is correct (handshake = encrypted session; authenticate = identity), but + the `did?: Did` field on `HandshakeResult` invites the wrong assumption. + +## #4 — Delegation `agent_pubkey` format undocumented; no SDK pubkey helper + +- **Severity:** Medium (integration friction on the headline Agent-Auth path) +- **Where:** `buildDelegationCredential({ agent_pubkey })`, `@terminal3/t3n-sdk@3.5.2` +- **Repro:** Building a delegation credential needs `agent_pubkey: Uint8Array`, + but nothing documents the encoding, and the SDK exports `eth_get_address` + (20-byte address) — not a public key. There is no `eth_get_pubkey` helper. +- **Expected:** A documented format + an SDK helper to derive the agent pubkey + from the same secret used for `metamask_sign` / `signAgentInvocation`. +- **Actual:** Determined empirically that `AGENT_PUBKEY_LEN === 33`, i.e. the + **compressed secp256k1** public key. Derived it via ethers + `new SigningKey(pk).compressedPublicKey` (ethers is already an SDK dep). + Works, but newcomers must reverse-engineer the length constant. + +## #5 — Doc gap: no worked example for the delegate → invoke crypto flow + +- **Severity:** Medium (docs gap on the SDK's headline feature) +- **Where:** README / `https://docs.terminal3.io` +- **Repro:** The delegation primitives (`buildDelegationCredential`, + `canonicaliseCredential`, `signCredential`, `buildInvocationPreimage`, + `signAgentInvocation`) ship with good doc comments but no end-to-end example + tying "user signs credential" → "agent signs invocation" → contract submit. +- **Expected:** One worked snippet (the `tee:payroll` flow exists as + `buildPayrollInvocation`, but it's payroll-specific and assumes a deployed + contract). +- **Actual:** Assembled the generic flow from the type defs; captured working + offline + testnet runs in `tools/step3-smoke.ts` for reference. From 64952285d9317e35c5609acf8eee92dd8c098f82 Mon Sep 17 00:00:00 2001 From: Divyansh Date: Fri, 19 Jun 2026 18:24:34 +0530 Subject: [PATCH 2/3] docs: update BUGS.md to match the new fork's verified content --- BUGS.md | 98 ++++++++++++++++++--------------------------------------- 1 file changed, 30 insertions(+), 68 deletions(-) diff --git a/BUGS.md b/BUGS.md index ac96702..9116f64 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,73 +1,35 @@ # BUGS.md — Terminal 3 SDK / docs friction log -Parallel bug-bounty track. Every claim-flow / SDK / docs friction encountered -while building gets logged here (severity, repro, expected vs actual). - -> TODO: email `devrel@terminal3.io` to confirm where bug submissions should go. +Verified against the [official ADK docs](https://docs.terminal3.io/developers/adk/overview/what-is-adk). Only confirmed issues listed. --- -## #1 — Spec says "API key", SDK has no API-key concept - -- **Severity:** Low (docs / onboarding friction) -- **Where:** Onboarding, `@terminal3/t3n-sdk@3.5.2` -- **Repro:** Following an "agent" mental model, you look for `T3N_API_KEY` to - authenticate the client. There is no such parameter. -- **Expected:** A documented API-key or token to identify the agent. -- **Actual:** The agent authenticates with an **Ethereum private key** (or OIDC). - The DID is *derived* from the key via `eth_get_address` → - `createEthAuthInput` → `client.authenticate`. There is no API key. -- **Resolution in this repo:** env var is `T3N_AGENT_PRIVATE_KEY`; the secret - never leaves the server and only the public DID/address is exposed. - -## #2 — No single end-to-end quickstart (API key -> first resolved DID) - -- **Severity:** Medium (docs gap, known) -- **Where:** `https://docs.terminal3.io` + package README -- **Repro:** As a newcomer, there is no one page that walks from - "I have credentials" to "I authenticated and resolved my first DID". -- **Expected:** A copy-paste quickstart that ends in a printed DID. -- **Actual:** You assemble it from the README snippets: - `setEnvironment` → `loadWasmComponent` → `eth_get_address` → - `new T3nClient({ handlers: { EthSign: metamask_sign(...) } })` → - `handshake()` → `authenticate(createEthAuthInput(address))` → `getDid()`. - Verified working against `testnet` (node `cn-api.sg.testnet.t3n.terminal3.io`). - -## #3 — `handshake()` returns `did: null` / `authenticated: false` - -- **Severity:** Low (mild surprise, not a blocker) -- **Where:** `T3nClient.handshake()` return value -- **Repro:** `const hr = await client.handshake()` → - `{ sessionId, expiry: 0, authenticated: false, did: null }`. -- **Expected (naively):** handshake yields the identity. -- **Actual:** Identity only appears after the separate `authenticate()` call. - This is correct (handshake = encrypted session; authenticate = identity), but - the `did?: Did` field on `HandshakeResult` invites the wrong assumption. - -## #4 — Delegation `agent_pubkey` format undocumented; no SDK pubkey helper - -- **Severity:** Medium (integration friction on the headline Agent-Auth path) -- **Where:** `buildDelegationCredential({ agent_pubkey })`, `@terminal3/t3n-sdk@3.5.2` -- **Repro:** Building a delegation credential needs `agent_pubkey: Uint8Array`, - but nothing documents the encoding, and the SDK exports `eth_get_address` - (20-byte address) — not a public key. There is no `eth_get_pubkey` helper. -- **Expected:** A documented format + an SDK helper to derive the agent pubkey - from the same secret used for `metamask_sign` / `signAgentInvocation`. -- **Actual:** Determined empirically that `AGENT_PUBKEY_LEN === 33`, i.e. the - **compressed secp256k1** public key. Derived it via ethers - `new SigningKey(pk).compressedPublicKey` (ethers is already an SDK dep). - Works, but newcomers must reverse-engineer the length constant. - -## #5 — Doc gap: no worked example for the delegate → invoke crypto flow - -- **Severity:** Medium (docs gap on the SDK's headline feature) -- **Where:** README / `https://docs.terminal3.io` -- **Repro:** The delegation primitives (`buildDelegationCredential`, - `canonicaliseCredential`, `signCredential`, `buildInvocationPreimage`, - `signAgentInvocation`) ship with good doc comments but no end-to-end example - tying "user signs credential" → "agent signs invocation" → contract submit. -- **Expected:** One worked snippet (the `tee:payroll` flow exists as - `buildPayrollInvocation`, but it's payroll-specific and assumes a deployed - contract). -- **Actual:** Assembled the generic flow from the type defs; captured working - offline + testnet runs in `tools/step3-smoke.ts` for reference. +## #1 — `agent_pubkey` format undocumented; no SDK pubkey helper + +`buildDelegationCredential({ agent_pubkey })` expects a `Uint8Array` but the +required encoding (33-byte compressed secp256k1) is nowhere in the docs. The SDK +exports `eth_get_address` (20-byte address) but no public-key helper — we had to +derive it via ethers `new SigningKey(pk).compressedPublicKey` and reverse-engineer +the `AGENT_PUBKEY_LEN === 33` constant. + +**Proof:** `buildDelegationCredential` and `agent_pubkey` return zero results +across the entire [docs index](https://docs.terminal3.io/llms.txt). The +[Delegate Access](https://docs.terminal3.io/t3n/data-owner-guide/delegate-access) +page only covers the GUI dashboard flow. The +[Invoke Contract](https://docs.terminal3.io/developers/adk/get-started/walkthrough/invoke-contract) +walkthrough uses `agentDid` (a DID string via `agent-auth-update`), which is a +different delegation model. + +## #2 — No worked example for the delegate → invoke crypto flow + +The SDK ships five delegation primitives (`buildDelegationCredential`, +`canonicaliseCredential`, `signCredential`, `buildInvocationPreimage`, +`signAgentInvocation`) but no end-to-end example tying "user signs credential → +agent signs invocation → contract submit". We assembled the flow from type defs +and captured it in `tools/step3-smoke.ts`. + +**Proof:** None of the five functions appear in any page listed in the +[docs index](https://docs.terminal3.io/llms.txt). The +[walkthrough](https://docs.terminal3.io/developers/adk/get-started/walkthrough/invoke-contract) +covers `execute()` / `executeAndDecode()` but not the underlying crypto +primitives. From 347ed928dfdfe888a45373b4d10f7b3993be0f7a Mon Sep 17 00:00:00 2001 From: Divyansh Date: Fri, 19 Jun 2026 23:48:45 +0530 Subject: [PATCH 3/3] feat: add secret database reset & seeding with warning disclaimer --- app/api/reset/route.ts | 84 ++++++++++++++++++++++++++++++ app/page.tsx | 114 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 app/api/reset/route.ts diff --git a/app/api/reset/route.ts b/app/api/reset/route.ts new file mode 100644 index 0000000..9779822 --- /dev/null +++ b/app/api/reset/route.ts @@ -0,0 +1,84 @@ +import { prisma } from "@/lib/store/prisma"; +import { BUYER_AUTH_PENDING } from "@/lib/store/types"; +import { safeJson, errorJson } from "@/lib/api/responses"; +import { NextResponse } from "next/server"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +export async function POST() { + // Production protection: prevent accidental wipes on production environment + if (process.env.NODE_ENV === "production" && process.env.ALLOW_DEMO_RESET !== "true") { + return new NextResponse("Forbidden: Database reset is disabled in production environment.", { status: 403 }); + } + + try { + // Idempotent reseed: clear children first (FK order), then parents. + await prisma.auditEntry.deleteMany(); + await prisma.billOfLading.deleteMany(); + await prisma.escrowTransfer.deleteMany(); + await prisma.contractTerms.deleteMany(); + await prisma.letterOfCredit.deleteMany(); + + // LC-1 — VALID: required port == target port, value $25,000 within $50,000 cap. + await prisma.letterOfCredit.create({ + data: { + buyerRef: BUYER_AUTH_PENDING, + exporterRef: "exporter-ref:acme-textiles-001", + valueCents: 2_500_000, + currency: "AUD", + targetPort: "Port of Rotterdam (NLRTM)", + state: "INITIATED", + terms: { + create: { + requiredPort: "Port of Rotterdam (NLRTM)", + requiredBolStatus: "DELIVERED", + maxValueCents: 5_000_000, + }, + }, + }, + }); + + // LC-2 — PORT MISMATCH: cargo targets Rotterdam, but terms require Hamburg. + await prisma.letterOfCredit.create({ + data: { + buyerRef: BUYER_AUTH_PENDING, + exporterRef: "exporter-ref:north-sea-foods-002", + valueCents: 1_800_000, + currency: "AUD", + targetPort: "Port of Rotterdam (NLRTM)", + state: "INITIATED", + terms: { + create: { + requiredPort: "Port of Hamburg (DEHAM)", + requiredBolStatus: "DELIVERED", + maxValueCents: 5_000_000, + }, + }, + }, + }); + + // LC-3 — OVER VALUE: value $95,000 exceeds the $50,000 contract cap. + await prisma.letterOfCredit.create({ + data: { + buyerRef: BUYER_AUTH_PENDING, + exporterRef: "exporter-ref:pacific-machinery-003", + valueCents: 9_500_000, + currency: "AUD", + targetPort: "Port of Singapore (SGSIN)", + state: "INITIATED", + terms: { + create: { + requiredPort: "Port of Singapore (SGSIN)", + requiredBolStatus: "DELIVERED", + maxValueCents: 5_000_000, + }, + }, + }, + }); + + return safeJson({ ok: true, message: "Database reset and seeded successfully." }); + } catch (err) { + return errorJson(err, 500); + } +} diff --git a/app/page.tsx b/app/page.tsx index a9220ec..7cbeed6 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -510,6 +510,11 @@ export default function Home() { const [formRequiredPort, setFormRequiredPort] = useState("Rotterdam"); const [formMaxLimit, setFormMaxLimit] = useState("25000"); + // Reset DB State + const [isResetOpen, setIsResetOpen] = useState(false); + const [confirmResetText, setConfirmResetText] = useState(""); + const [isResetting, setIsResetting] = useState(false); + const handlePortChange = (val: string) => { setFormPort(val); if (!customizePolicy) { @@ -566,6 +571,28 @@ export default function Home() { } } + async function handleResetDb() { + setIsResetting(true); + setError(null); + try { + const response = await fetch("/api/reset", { + method: "POST", + }); + if (!response.ok) { + const errData = await response.json(); + throw new Error(errData.error || "Failed to reset database"); + } + setIsResetOpen(false); + setConfirmResetText(""); + setSelectedId(null); + await refresh(); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setIsResetting(false); + } + } + const selectedLc = useMemo( () => lcs.find((lc) => lc.id === selectedId) ?? lcs[0], [lcs, selectedId], @@ -666,7 +693,16 @@ export default function Home() {
-

Autonomous trade finance node

+

+ Autonomous trade finance node + +

Letter of Credit Agent Console

@@ -1093,6 +1129,82 @@ export default function Home() {
)} + {isResetOpen && ( + <> +
{ + if (!isResetting) { + setIsResetOpen(false); + setConfirmResetText(""); + } + }} + className="fixed inset-0 bg-black/85 backdrop-blur-sm z-[60] transition-opacity duration-300 animate-fade-in" + /> +
+
+ ⚠️ +
+

Dangerous Administrative Action

+

Reset Demo Database

+
+
+ +
+
+ Warning & Disclaimer: + This action is intended strictly for the demo version of the application. It will permanently delete: +
    +
  • All Letters of Credit contracts
  • +
  • All simulated Bills of Lading
  • +
  • All escrow hold & payout records
  • +
  • All cryptographic audit ledger logs
  • +
+
+ +

+ Once executed, the database will be seeded back to its original 3 demo contracts. There is no undo. +

+ +
+ + setConfirmResetText(e.target.value)} + placeholder="RESET" + disabled={isResetting} + className="w-full bg-black/60 border border-neutral-900 rounded-sm px-3 py-2 text-neutral-200 placeholder-neutral-800 focus:outline-none focus:border-red-500/50 transition font-mono uppercase tracking-wider text-center" + /> +
+ +
+ + +
+
+
+ + )} ); }