chore: align dev with current staging (gRPC migration + docs merged into staging's security-delete work)#483
Draft
harrymove-ctrl wants to merge 29 commits into
Draft
chore: align dev with current staging (gRPC migration + docs merged into staging's security-delete work)#483harrymove-ctrl wants to merge 29 commits into
harrymove-ctrl wants to merge 29 commits into
Conversation
staging <- dev
staging <- dev
…protocol (#281) The release workflows used `npm publish`, which ships package.json verbatim and cannot rewrite the `workspace:` protocol. As a result oc-memwal was published with `"@mysten-incubation/memwal": "workspace:*"`, which fails to install outside the monorepo (`Unsupported URL Type 'workspace:'`). Switch release-oc-memwal, release-mcp, and release-sdk to `pnpm publish` (--no-git-checks), which rewrites `workspace:*` to the concrete dependency version at pack time. Bump oc-memwal to 0.0.5 so a working version is published; 0.0.4 on npm remains broken and should be deprecated.
…ce (#351) Mainnet writes were failing 100% at the register_sponsor phase with an Enoki dry_run_failed -> MoveAbort in 0x2::coin::destroy_zero (ENonZero). Root cause is not Enoki: @mysten/walrus '#withWal' pre-funds an *exact* WAL payment computed from the client's cached systemState price, then asserts the coin is empty via coin::destroy_zero. When mainnet storage/write price drifts down between the cached read and execution, the contract deducts less WAL than we split off and the leftover trips destroy_zero. Verified on prod: on-chain price moved 71464->70922 within ~15 min, and a fresh relayer boot served 6 writes before the same client flipped to 100% destroy_zero at the next price move. Two gaps let this fail hard instead of self-healing: - The sidecar's auto-refresh only fired on isMoveAbortBalanceSplit ('balance' + 'split'); this message says destroy_zero, so refreshWalrusClient never ran. - The Rust worker swept it into the MoveAbort -> Permanent catch, so Apalis Dead-marked every write with no retry. Fix: - enoki.ts: add isMoveAbortWalDestroyZero detector. - walrus-upload.ts: refreshWalrusClient() on the destroy_zero abort so the retry rebuilds against the live price. - jobs.rs: classify the register destroy_zero abort as Transient (retryable), disjoint from the balance::split gas-budget path. - config.ts: drop WALRUS_CLIENT_MAX_AGE_MS default 30m -> 60s so the cached price tracks a live-drifting mainnet price (still env-overridable). Tests: new TS detector suite (7) + Rust classification test using the verbatim prod error. Full suites green (85 TS, 40 jobs).
The sidecar hardcoded getJsonRpcFullnodeUrl(mainnet) and ignored the SUI_RPC_URL env, so it could not be pointed at a dedicated RPC when the public fullnode pool degrades. Observed today: the public pool served stale reads (a blob certified at 00:49:14 read back as 'does not exist' at 00:49:49, 35s later), failing uploads at get_blob/certify/metadata. Honor an explicit SUI_RPC_URL override so ops can switch to a healthy RPC via env; fall back to the network default.
* docs: add guide for deleting old memories * docs: hide delete-old-memories guide from navigation Remove the Guides tab (which only contained the delete-old-memories guide) from the docs navigation so the page is no longer surfaced in the site. The guide's markdown file is kept in place so the content is preserved and can be re-enabled by restoring the navigation entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4ARRUn69UZpxyvaFFUfuQ * docs: remove Seal encryption claims from Walrus Memory overview --------- Co-authored-by: Reem Sabawi <reem.sabawi@mystenlabs.com> Co-authored-by: Claude <noreply@anthropic.com>
…ries Adds a subsystem for permanently deleting the memory blobs written by the pre-upgrade encryption scheme. The server resolves which blobs a wallet owns, builds and sponsors the delete transaction, and the user signs it; the blob object is destroyed on chain and its data is no longer retrievable from Walrus. Users pay no gas. Server - `/api/security-delete-auth/*` — wallet-signature auth minting a short-lived token. The address is derived from the signature, never taken from the request. - `/api/security-deletable-blobs` — the wallet's deletable blobs, resolved from chain rather than from the database, so a row can never claim ownership the chain does not agree with. - `/api/security-deletions` — prepare / submit / cancel. The delete PTB is built entirely server-side against a hard-coded `system::delete_blob` target; the client only ever returns a signature over bytes it did not choose. - Background reconciler and object resolver to settle in-flight batches and keep tracking state in step with the chain. - Deletion is tracked in a separate legacy database (`LEGACY_DB_URL`); the main database is untouched. App - A "Delete old memories" card listing the wallet's remaining old memories, with an in-browser decrypt preview so a user can read a memory before destroying it, single and bulk delete, and progress/history. Two chain-facing details are worth calling out, because both are invisible on a freshly-published local network and destroy data on a live one: - `Blob.storage.end_epoch` is a WALRUS epoch and must never be compared against the Sui ledger epoch. The two clocks are independent and diverge without limit, so the comparison marks live blobs expired — and expired is terminal, so the user's data would stay on Walrus with no way left to remove it. - The Walrus package id must be read from the System object at runtime. Walrus upgrades its package; a pinned id makes every delete abort on chain. The id in config is a fallback and a drift signal only. It cannot simply be repointed at the new package: the same value also types the owned-object filter, and Move types keep naming the package they were published in. `SuiEpoch`, `WalrusEpoch` and `WalrusCallPackage` are distinct types precisely so that neither mistake can be expressed. Both were previously bare primitives. Everything is off by default (`ENABLE_MEMORY_DELETION`, `ENABLE_SECURITY_DELETE`, `VITE_SECURITY_DELETE_ENABLED`), behind a boot guard that refuses a partial configuration. The subsystem talks to Sui over gRPC; on mainnet the existing JSON-RPC paths are untouched. 410 tests (381 unit, 29 Postgres-backed). Verified end to end against live Sui and Walrus testnet: prepare, sponsor, sign, execute, and the blob object absent from chain afterwards. Co-authored-by: Daniel Lam <daniellamhoang@gmail.com> Co-authored-by: hungtranphamminh <tranphamminhhung65@gmail.com>
Brings the Walrus register `destroy_zero` stale-WAL-price recovery and the explicit `SUI_RPC_URL` override, plus the accompanying docs and CI. One conflict, in `services/server/scripts/sidecar/config.ts`: both sides keep `SUI_RPC_URL` with the same fallback, and this branch adds `SUI_GRPC_URL` and a validating `parseSuiNetwork` on top. Kept that structure and took the incoming comment, which documents why the override exists — the public fullnode has served stale reads that fail uploads at get_blob / certify. `jobs.rs` and the sidecar's walrus-upload route auto-merged; the `destroy_zero` reclassification is not duplicated.
…ndpoint The public fullnode.mainnet.sui.io pool serves stale/slow reads under load (read-after-write lag measured at 40-70s), which breaks flows that read state their own transaction just wrote. Point the relayer's SUI_RPC_URL at rpc-mainnet.suiscan.xyz and give the app a matching VITE_SUI_RPC_URL override (active network only; other networks keep the public default).
Defaults mainnet to the dedicated JSON-RPC endpoint rather than the public fullnode, whose pool serves stale reads under load. `VITE_SUI_RPC_URL` now overrides the endpoint for the active network, mirroring the relayer's `SUI_RPC_URL`. Merged clean. Also corrects two stale comments in `config.ts` that still described the app's shared client as gRPC-only.
feat(security-delete): let users permanently delete their old V1 memo…
fix(app): hide preview resolution text
fix(api): pause memory uploads
fix(api): return 503 for paused uploads
fix(app): update delete memory tabs
fix(app): distinguish delete tabs
the evolved security-delete feature and relayer hardening Both branches had diverged substantially (112 files, +18.7k/-1k lines) since dev's PR #355 gRPC migration landed without ever reaching staging, while staging independently rebuilt the delete-old-memories feature into "security_delete" (native Sui gRPC client in services/server/src/sui/, dedicated auth/error/store modules) and hardened the relayer (destroy_zero recovery, dedicated JSON-RPC RPC provider). This merge keeps both lines of work; base = staging (dev has no unique commits it kept since #389 was closed unmerged). Conflict resolutions (15 files): - App.tsx / config.ts / suiClientCompat.ts: kept staging's deliberate architecture — the shared SuiClientProvider stays on JSON-RPC (documented reason: every JSON-RPC-shaped consumer would need dual-transport compat otherwise), gRPC is scoped to the security-delete subsystem. Removed dev's now-dead top-level createClientForNetwork/gRPC swap and its unused imports. - Dockerfile: kept both VITE_SUI_RPC_URL (staging) and VITE_SUI_GRPC_URL (dev) ARG/ENV pairs. - vite.config.ts: kept staging's dev-only E2E wallet injector addition (dev didn't touch this file). - docs/guides/delete-old-memories.md: took dev's later wording fix ("Stored" status label) since it matches what SecurityDeleteSection.tsx actually renders; staging's "At risk" wording was stale. - services/server/{auth,main,storage/sui}.rs, types.rs: kept staging's side throughout — it added a real fix (validate_memwal_account_type, rejecting objects that aren't actually a MemWalAccount before trusting their fields) that dev's simpler verify_delegate_key_onchain lacks. types.rs keeps both doc comments (merged, not a pick). - Cargo.toml/.lock: kept staging's exact-pinned crate set (needed by the new sui/ native client module); lockfile regenerated via cargo build. - sidecar clients.ts/config.ts/routes/walrus-query.ts: kept staging's hardened testnet policy (SUI_GRPC_URL mandatory on testnet, boot-time validateSuiTransportPolicy guard, suiJsonRpcClient forced null on testnet) over dev's opt-in/lenient version. IMPORTANT — found during review, not introduced by this merge: staging's account.move currently lacks security fix 97754fc ("scope seal_approve authorization to the account owner", dev-only, marked "code-only; not deployed" at the time). Without it, a registered delegate's seal_approve authorization does not bind the requested key `id` to the account they're a delegate on, so `is_delegate_address` alone can satisfy the assert regardless of whose data the id belongs to. This merge's Move source now has dev's fix (git auto-merged it cleanly since staging never touched this function), plus its regression test (test_seal_approve_delegate_rejects_unrelated_id, previously absent from staging). Needs a decision on whether/where this contract change has already been published on-chain. Verified after resolution: cargo check clean; cargo test 273/278 (5 failures are pre-existing local DB-pool timeouts, unrelated); sidecar 89/89; app tsc + vite build + 66/66 vitest; Move contract 48/48.
| const promise = createSession(opts.suiClient, opts.address, opts.signPersonalMessage) | ||
| sessions.set(key, promise) | ||
| try { return await promise } catch (error) { | ||
| if (sessions.get(key) === promise) sessions.delete(key) |
harrymove-ctrl
force-pushed
the
chore/staging-baseline
branch
from
July 22, 2026 04:27
fe91f78 to
cab99be
Compare
…C_URL relayer docs, #482 agent-memory developer guides)
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
This PR is the new dev baseline built on staging as the standard: base is
staging, plus the validdev-only parts. It introduces no new gRPC — the gRPC in this diff is whatstagingalready ships (690888b9) reconciled withdev's #355, and wherever the two branches disagreed, conflicts were resolved instaging's favor.Context:
devandstaginghad diverged substantially (112 files, +18.7k/−1k lines) — the gRPC migration from #355 landed ondevbut never reachedstaging, whilestagingindependently rebuilt the delete-old-memories feature into thesecurity_deletesubsystem and hardened the relayer.What dev adds on top of staging (19 files)
Documentation (10):
docs/guides/delete-memories-programmatically.mddocs/sdk/headless-setup.mddocs/sdk/codebase-memory.mddocs/fundamentals/concepts/verifiable-memory.mddocs/relayer/overview.md,docs/relayer/self-hosting.mdSUI_GRPC_URLdocumentation (#481)docs/reference/environment-variables.md,docs/reference/configuration.mddocs/docs.jsondocs/guides/delete-old-memories.mdSmart contract (2):
services/contract/sources/account.move+tests/account_tests.move— theseal_approveauthorization-scoping fix with its regression test (test_seal_approve_delegate_rejects_unrelated_id).Supporting changes (7):
services/server/src/types.rs(documents thatsui_rpc::Clientmust not be rebuilt per request),apps/app/src/utils/suiClientCompat.ts(architecture note updated).ConnectMcp.tsxandSetupWizard.tsxnow use the sharedsuiClientCompathelpers instead of the deletedsuiFields.ts— same behavior (JSON-RPC), but the pages no longer assume one transport shape.sidecar/retry/rpc.tsadds gRPC-aware retryable status codes (UNAVAILABLE,RESOURCE_EXHAUSTED,DEADLINE_EXCEEDED, …) — without it, the gRPC write path staging already uses silently stops retrying;useSponsoredTransaction.tsadds retry/backoff on the/sponsorcall and settles on the always-sponsored flow.Note: the GitHub "Files changed" tab diffs against the base (
dev), so it shows a different, larger set — the list above is the delta againststaging, which is the meaningful one under the "staging is standard" model.Conflict resolutions
staging's architectureSuiClientProviderstays JSON-RPC; gRPC remains scoped to the security-delete subsystem's own client.dev's unconditionalcreateClientForNetworkswap was removed.staging's hardeningvalidate_memwal_account_typerejects objects that aren't actually aMemWalAccountbefore trusting their fields.staging's stricter stancevalidateSuiTransportPolicyguard,suiJsonRpcClientforced tonull.Cargo.toml/Cargo.lockstaging's exact pinssui/client module.The full file-by-file resolution list is in the merge commit message (
03d1f72).Verification
Deployed environment — the Railway
devenvironment runs this exact branch on bothapp(dev.memwal.ai) andrelayer(relayer.dev.memwal.ai):delegate key verified onchain (gRPC)on/api/remember; no failed phasesTest suites
cargo checkclean;cargo test273/278 (the 5 failures are pre-existing local DB-pool timeouts, unrelated)npm test89/89tsc -b,vite build+ verify-dist-assets, vitest 66/66