Add signed invites, hang links, multi-share, and EXTENSIONS.md
- Signed invites: waste: URI gains inviter+sig fields (Ed25519); hello carries the invite so receiving peers can verify against known keys - RequireInvite per-network flag: rejects peers without valid signed invite - Hash-based hang links: #waste:base64 fragment pre-fills join form without server-side leakage of network name - Multi-share: shares.json (daemon) + waste_shares localStorage (browser); IPC add_share/remove_share/list_shares commands - EXTENSIONS.md: addendum documenting all waste-go protocol deviations from YAW/2; all extensions are additive and backward compatible Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,17 @@ export function Sidebar() {
|
||||
const displayId = localPeer?.id ?? masterId ?? ''
|
||||
const card = displayId ? makeYawCard(displayId, displayAlias) : null
|
||||
|
||||
function copyHangLink() {
|
||||
const net = networks.find(n => n.network_id === activeNetworkId)
|
||||
if (!net) return
|
||||
const cfg = (window as unknown as { WASTE_CONFIG?: { signalURL?: string } }).WASTE_CONFIG
|
||||
const anchor = cfg?.signalURL ?? ''
|
||||
const payload = btoa(JSON.stringify({ network: net.network_name, anchor }))
|
||||
.replace(/\+/g, '-').replace(/\//g, '_')
|
||||
const url = `${window.location.origin}/#waste:${payload}`
|
||||
navigator.clipboard?.writeText(url)
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
const clearId = window.confirm('Also clear your identity keypair? (Cannot be undone — export a backup first if you want to keep it.)')
|
||||
logout(clearId)
|
||||
@@ -82,7 +93,12 @@ export function Sidebar() {
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
<span className="sidebar-label">Networks</span>
|
||||
<div className="sidebar-label-row">
|
||||
<span className="sidebar-label">Networks</span>
|
||||
{activeNetworkId && (
|
||||
<button className="sidebar-add" onClick={copyHangLink} title="Copy hang link (pre-fills join form, no invite required)">🔗</button>
|
||||
)}
|
||||
</div>
|
||||
{networks.map(n => (
|
||||
<button
|
||||
key={n.network_id}
|
||||
|
||||
@@ -14,11 +14,17 @@ interface Props {
|
||||
// ?a=<url> anchor URL hint
|
||||
function parseInviteParams(): { network: string; netHash: string; anchor: string; inviteString: string } {
|
||||
const p = new URLSearchParams(window.location.search)
|
||||
const inviteString = p.get('invite') ?? ''
|
||||
let inviteString = p.get('invite') ?? ''
|
||||
let network = p.get('n') ?? p.get('network') ?? ''
|
||||
let netHash = p.get('net') ?? ''
|
||||
let anchor = p.get('a') ?? p.get('anchor') ?? ''
|
||||
|
||||
// Hash-based hang link: https://host/#waste:eyJ... (opaque, not sent to server)
|
||||
const hash = window.location.hash.slice(1) // strip leading #
|
||||
if (!inviteString && hash.startsWith('waste:')) {
|
||||
inviteString = hash
|
||||
}
|
||||
|
||||
if (inviteString.startsWith('waste:')) {
|
||||
try {
|
||||
const json = JSON.parse(atob(inviteString.slice(6).replace(/-/g, '+').replace(/_/g, '/')))
|
||||
|
||||
Reference in New Issue
Block a user