Skip to content

feat: implement signet unlink with web API - #321

Merged
blockchain-maxis merged 2 commits into
blockchain-maxis:mainfrom
Hussman256:feature/issue-261
Sep 4, 2026
Merged

feat: implement signet unlink with web API#321
blockchain-maxis merged 2 commits into
blockchain-maxis:mainfrom
Hussman256:feature/issue-261

Conversation

@Hussman256

Copy link
Copy Markdown
Contributor

Fixes #261. This PR implements the signet unlink command via a web API call.

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

@Anambraboi-1 is attempting to deploy a commit to the blockchainmaxis-8449's projects Team on Vercel.

A member of the Team first needs to authorize it.

@netlify

netlify Bot commented Aug 30, 2026

Copy link
Copy Markdown

Deploy Preview for stellar-signet ready!

Name Link
🔨 Latest commit 28a3786
🔍 Latest deploy log https://app.netlify.com/projects/stellar-signet/deploys/6a9afd4b91c1040007e57ebd
😎 Deploy Preview https://deploy-preview-321--stellar-signet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@blockchain-maxis blockchain-maxis left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same call as #320, and for the same reasons — flagging it here so you don't have to infer it from that thread.

The command itself is right in the ways that matter for #261: it proves ownership by signing a challenge rather than trusting the caller's word, it fails closed when the signature doesn't verify, and the --yes / confirm() handling matches the acceptance criterion exactly. The unlink flow is sound.

What blocks it:

1. Architecture. This adds to apps/cli (TypeScript). #251 scaffolds the CLI as a Go module at cli/, and the rest of the series — #335, #369, #370, #358, #342 and others — is already building there. npx @signet/cli unlink still works under that plan; #293 covers cross-compiling the Go binary behind an npm wrapper.

2. getSecret() puts the deploy key's secret in process memory. #253 is explicit that the CLI should never hold key material — stellar tx sign --sign-with-key <identity> signs without the secret leaving stellar, and brings hardware-wallet support with it. This applies to unlink exactly as it does to link, since both sign a challenge.

One thing specific to this PR, worth carrying over to whichever implementation lands: /api/cli/challenge mints a challenge for any pubkey the caller names, and /api/cli/unlink accepts it back with a signature. That's the right shape, but the challenge needs to be bound to the operation and origin it was issued for — otherwise a challenge minted for one purpose can be replayed against another. #269 covers exactly this domain separation, and #343 is open against it.

Leaving this open. If you want to keep going, signet unlink inside cli/ on top of #335's scaffold is unclaimed and the series needs it.

blockchain-maxis#261: a link with no unlink is a one-way door. A rotated or compromised deploy
key keeps feeding a profile, and the only way to stop it is the dashboard —
which is no help to whoever holds the key but not the handle.

`signet unlink` proves control of the deploy key by signing a SEP-10 challenge,
exactly as `link` does, and `POST /api/cli/unlink` removes the binding.

**Why this needs one proof where linking needs two.** `pair/complete` requires
the handle owner's browser session *and* a signature, because attaching a
wallet makes a claim about someone's profile. Detaching makes no claim: it
withdraws one. The party controlling the key is the party whose attestation the
profile was displaying, and letting them take it back is the point — requiring
the handle owner's consent too would mean a developer who left a team could not
stop their key feeding a profile they no longer control. The asymmetry is safe
in the direction that matters: removing a wallet can only reduce what a profile
claims, never attribute work to anybody.

Refusals: an unsigned or wrongly-signed challenge (`bad-challenge`), a wallet
no profile holds (`not-linked`), and the primary wallet — which is the
handle→wallet claim itself, so releasing it is an on-chain registry operation
rather than a row delete, the same refusal the dashboard gives.

Verify → spend → delete, in that order, mirroring `completePairing`: consuming
the challenge any earlier would let a bystander who merely saw it (it is not
secret) burn it with a junk signature and lock the real caller out. A test pins
that.

**Shared challenge spending.** The review on this PR flagged that a challenge
minted for one operation can be replayed against another (blockchain-maxis#269, tracked in
blockchain-maxis#343). Full binding needs the challenge to carry its purpose and cannot be
retrofitted here — but the cheaper version of that bug was about to be
introduced: with pairing spending `pair:<hash>` and unlink spending
`unlink:<hash>`, one intercepted envelope could be redeemed once against each.
Both now share `sep10-challenge:<hash>`, so it is one use in total. The new
module says explicitly that this is single-use, not operation binding, and
points at blockchain-maxis#343.

Confirms before acting unless `--yes`, naming the key rather than asking about
"your wallet" — the resolved identity may not be the one the developer
expected. Anything but an explicit y/yes is a no; a prompt that reads a stray
newline as consent is not a confirmation, and a test covers that.

The branch's TypeScript CLI under apps/cli is dropped, along with its
getSecret() — signing goes through `stellar tx sign` (keys.SignChallenge), so
the secret never enters this process.
@blockchain-maxis
blockchain-maxis merged commit 011f6e5 into blockchain-maxis:main Sep 4, 2026
10 of 11 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.

Add signet unlink to detach a deploy wallet from the terminal

3 participants