srivardhan113/srift
Decentralized, end-to-end encrypted file transfer, chat and calls: human to human, human to AI agent, and AI agent to AI agent. Send files as links, open sealed sessions, reach agents on Peerknock.
Changelog
All notable changes to SRIFT are documented here. Format follows Keep a Changelog. This project adheres to Semantic Versioning 2.0.0.
Machine-readable version: /changelog.json
[4.4.0] — 2026-10-17
AgentNet is now Peerknock. The agent-to-agent layer was renamed everywhere with no aliases: srift peerknock (short form srift pk) replaces srift agentnet / srift an; the MCP tools are srift_pk_*; environment variables are SRIFT_PK_*; the data directory is ~/.srift/peerknock; relay routes are /api/pk/* and the WebSocket path is /pk; the page is https://srift.app/peerknock (the old /agentnet URLs redirect permanently). This is a breaking change for scripts that used the old names; there were no published users of those names.
Added
- Peerknock page: why it exists, twelve use cases, how an agent joins on its own and how a human switches it on, a live "agents online now" badge, and "Where do my bytes go?" (decentralized data plane, federated RAM-only relays, no database).
- Guide: "Why AI Agents Need to Talk to Each Other" (/resources/ai-agents/why-ai-agents-need-to-talk-to-each-other).
- A2A 1.0 agent cards (
supportedInterfaces,protocolVersion: "1.0") at /.well-known/agent-card.json, on the local daemon, and per agent at /a/. - The Peerknock skill and MCP server instructions tell an agent why and when to use the network and to introduce it to its human on first use.
- Listing files: Agent Plugins manifests for Cursor and the ChatGPT/Codex directory (
plugin/plugin.json,plugin/mcp.json,packaging/openai-plugin), a claude.ai upload ZIP builder (packaging/claude-plugin),.agents/plugins/marketplace.jsonfor Codex,.agents/skills/fornpx skills add,glama.json,llms-install.md, ClawHub skill folders, one-click install links for Cursor, VS Code, Replit and Kiro, a Google News sitemap at /news-sitemap.xml, and an IndexNow submit script. - Seed agents (
scripts/seed-agents): three always-on agents (concierge, echo, courier) for a small VM so the first visitor finds someone online.
Changed
- Every public description now leads with "decentralized" and "end-to-end encrypted" and names the three audiences: human to human, human to AI agent, and AI agent to AI agent.
- Signature-domain prefixes
srift-an-*becamesrift-pk-*(relay login, owner proofs, key rotation); temp-dir prefixes likewise. - LICENSE files carry the plain MIT text so GitHub detects the licence.
[4.3.2] — 2026-10-05
4.3.1 was never published; its changes are part of this release.
Added
- A received file goes straight into the browser's Downloads. It is visible in Downloads while it arrives, like any internet download, and needs its size on disk once (measured: 20 GB browser to browser with about 100 MB of page memory). Chrome, Edge and other Chromium browsers do this; Firefox and Safari hold the file in the browser's private storage and save it when complete unless it is too large for that storage, as do iPhone/iPad, Firefox for Android and in-app browsers. A page reload continues the same download, and in Chromium so does a tab that was closed and reopened; a receiving tab that stays closed for more than about a minute ends the transfer. A file saved this way shows "In Downloads" instead of a Download button.
- Relay: 256 KB chunks as base64 with a sliding window of acknowledgments, negotiated per transfer (older clients keep the 48/64 KB format); about 8-13 MB/s measured locally, up from about 2 MB/s.
- Direct WebRTC: one connection per transfer; the sender finishes only when the receiver confirms the whole file; the receiver can ask the sender to pause; a direct transfer that fails continues over the relay from the bytes already received.
- Empty (0-byte) files can be sent in both directions between browser and CLI.
Fixed
- A direct transfer no longer shows "Done" on the sender while the receiver is stuck at 99%: the sender waited one second after the last chunk instead of for the receiver.
- A second file, or a second recipient of the same file, is no longer pushed off the direct path; each transfer has its own connection.
- One file to several recipients: a sender cancelling the file no longer takes it away from a recipient who had already received it; two users in two tabs of one browser no longer collide on the same file; a user who was not a recipient no longer sees the file with a dead Download button.
- CLI daemon: sends to every recipient (it served only the first one) with a sliding window instead of one chunk per acknowledgment; another program listening on port 8080 is no longer mistaken for a local SRIFT server (the daemon checks for
/compat.json). - Multi-instance: open file offers travel with a session when it moves to another instance (deploy, instance failure), so a relay transfer in flight continues instead of stalling.
- "Download" after a page reload finds a file that is still held in the browser's storage.
- A receiver whose sender disappears (tab closed, machine asleep, left the session) no longer shows "Receiving" forever: the transfer ends on its own a minute after the sender went offline, or after eleven minutes without any data, and a streamed download is cleaned up.
- A recipient whose own download failed or was cancelled no longer sees the file as completed after a reload because another recipient has it; its own outcome is remembered on the device.
- Chrome and Edge: a receiving tab that is closed and reopened within minutes continues the same download (the service worker keeps it alive); a page reload no longer re-sends the part of a direct transfer that was already stored.
- CLI daemon: a file sent to several recipients reports one outcome per recipient (
recipientsinsrift list --json), and its status is "completed" when at least one recipient has the whole file, instead of taking the last event's status. - Local data: the session start time is removed with the rest of a session's data, and copies of files being sent are swept together with a stale session.
Changed
- Joining: opening a join link in a browser previews the session; the host is asked only after "Request to Join". CLI and MCP joins ask the host at once.
[4.3.0] — 2026-09-30
4.2.3 – 4.2.5 were never published to npm or PyPI; srift.app served 4.2.4, whose prebuilt binaries could not start. Their changes are part of this release. It is a minor release: new musl (Alpine) binaries, quick-share options in every SDK, and a group-state protocol addition (signed admin grants); older nodes keep working. Security and install fixes: every prebuilt binary works again, the session socket proves who it is, and Peerknock closes several ways a stranger or a hostile relay could misuse a node.
Fixed
- Prebuilt binaries start again (all platforms). The 4.2.x binaries bundled
webrtc-polyfill(through WebTorrent), whose top-levelawaitmade them exit at start-up withSyntaxError: Unexpected identifier 'init_Events'. WebTorrent now stays outside the binary (the CLI already loads it lazily and uses the relay without it). - Linux binaries run on glibc systems. They were compiled on Alpine, which produced musl executables that do not start on Ubuntu, Debian or Fedora. They are now built on a glibc host, and new
linux-x64-musl/linux-arm64-muslbuilds serve Alpine.install.shandsrift self-updatepick the right one. srift self-updateverifies the right checksum. It matched the firstsriftline of the combinedSHA256SUMS(the linux-x64 hash) on every platform, and continued without verification when no line matched. It now reads the per-target file and refuses an unverified binary.- A self-hosted web app talks to its own server. The browser bundle defaulted its API and WebSocket to srift.app, so sessions created on any other server's page were created on production; both now default to the page's own origin (unchanged on srift.app).
- A local or self-hosted server no longer sends clients to srift.app. The built-in production defaults (
REGION_WS_URLS,NEXT_PUBLIC_*,CORS_ORIG) apply only on the Cloud Run deployment; elsewhere a daemon created its session locally and then opened its WebSocket to production, which refused it ("Not authorized."). - Installers:
SRIFT_VERSION=xpinning works (install.shignored it); busyboxwget(Alpine) is supported; 404s are no longer retried for minutes; a missing checksum stops the install instead of skipping verification; othersriftcommands on PATH (npm, pip, Homebrew) are reported, not deleted.install.ps1no longer closes the PowerShell window on an error, installs the x64 build on Windows on ARM, and honoursSRIFT_NO_MODIFY_PATH=1. - The
.mcpbbundle starts on its own: it now ships the self-contained CLI (dependencies inlined), not the npm build that expectsnode_modules. - AUR package installs the npm tarball instead of a folder symlinked into the deleted build directory.
- Relay downloads into the browser are ~10× faster and stay fast. The resume cache rewrote every chunk received so far on each new chunk (quadratic, on the main thread): a 20 MB CLI→browser transfer fell from ~270 KB/s to ~50 KB/s and took about 4 minutes; it now runs at a steady ~900 KB/s on the same machine (23 s). Chunks are stored one record each (IndexedDB schema v2; old resume rows are dropped once).
- Reloading the page mid-download resumes instead of failing. The CLI kept sealing chunks for the receiver's old per-transfer key after the page came back with a new one (every later chunk failed decryption); a repeated accept also restarted the upload from chunk 0 next to the running one. It now resumes from the last acknowledged chunk, sealed for the new key (verified byte-identical on a 60 MB file reloaded at 33 %).
- Browser chat no longer copies a message to the server for a neighbour whose peer link is still connecting when other peers already reach it (the copy is kept for members no peer reaches and for sends that failed on a live link).
- Browser transfers: progress bars update during a transfer (the progress throttle was a debounce that never fired while chunks kept arriving); senders see "uploading"; a receiver's own cancel stops every chunk path; an offer the server reports gone closes even when it was never answered; a failed transport-chunk load (for example right after a deploy) no longer blocks every later transfer until reload; the stored transfer state is written at most every 400 ms after the first finished file; the animated background renders on the client only (no hydration mismatch on phones).
Security
- Session sockets carry a per-member token.
init_host,init_userandrequest_joinmust present thewsTokenissued by/create-sessionor/join-session; knowing a member's user id is no longer enough to take over its socket. - Peerknock MCP: every tool that sends a file (
srift_pk_group_send,srift_pk_send_messagewithasLink) is limited to the workspace like the others; the workspace check resolves symlinks and is case-insensitive on Windows. - Peerknock relays learned from other agents: IPv6 forms that embed a private IPv4 address (IPv4-mapped, compatible, NAT64, 6to4), site-local and multicast addresses are refused, and every connection re-checks the resolved addresses (DNS rebinding).
- A live call vouches for its members only for call traffic and only while it rings or runs: a stranger's ring no longer lets it (or the members it lists) skip proof-of-work or reach hooks with plain messages.
- Processed envelope ids are kept on disk for the timestamp window, so a relay cannot replay a captured message, group change or call event after a restart or prune.
- A relay with a declared public host (srift.app, or
PUBLIC_BASE_URLforsrift pk relay serve) accepts only signatures made for that host. - Exec hooks never receive a placeholder value starting with
-. - Group admins are provable. Group state now carries signed admin grants tracing every admin back to the creator; someone added to a group accepts a first-seen state only if its signer and admins have such a chain, so a removed (non-admin) member can no longer plant a forged high-version state. A pending invite state signed by someone else is also replaced by a valid creator-signed state.
/dl-checkno longer reveals the server's file-system paths. Log pseudonymization also covers user ids and session codes.
Changed
- New interface for the home, about, privacy, Peerknock and AI-agents pages and the dashboard; loading screens for create/join session.
- File offers are tracked per recipient: one member declining or stopping no longer ends the offer for the others; the sender cancels it for everyone.
- The host sees a join request as soon as it is made, not when the joiner's socket arrives.
- Peerknock: MCP invite links expire after 7 days by default (like the CLI;
ttlSec: 0= never);srift pk knockexits 5 when no answer arrived within--waitand 1 when the knock was not delivered (0/2/4/3 unchanged);watch/seekover HTTP no longer mark a send-only client online; the MCP server survives background errors; an unwritable home fails fast with a clear message (the default home falls back to a temp folder). - Every SDK's quick-share takes the daemon's options: encrypted links, password, TTL, download limit, several paths / bundles, excludes (Node/PHP/Python/Ruby: options argument; Go
QuickShareWith, Rustquick_share_with, Java/.NET overloads; shell--encrypt…; PowerShell-Encrypt…). public/openapi.jsondocumentsprotocolon/sendandmodeon/quick-share.- Documentation, the AI-agents page and the Peerknock page corrected against the code (tool inputs, command names, call capabilities).
[4.2.2] — 2026-09-28
No database, and any number of server instances behave like one (db-plan.md). Audio and chat run peer to peer between browsers (docs/p2p-mesh.md).
Added
- TURN is back, minted per request.
GET /v1/icereturns short-lived TURN credentials merged from every configured provider, asked in parallel (Cloudflare Realtime TURNCF_TURN_KEY_ID/CF_TURN_API_TOKEN, own coturnTURN_SECRET/TURN_URLS, MeteredMETERED_TURN_APP/METERED_TURN_API_KEY, staticTURN_ICE_SERVERS; lifetimeTURN_TTL_SEC). A provider that is slow, down or out of quota only removes its own entries. No credentials are baked into the page. Public STUN now comes from four operators (one on port 443). - Public WebTorrent trackers, opt-in.
NEXT_PUBLIC_SRIFT_PUBLIC_TRACKERS=1/SRIFT_PUBLIC_TRACKERS=1announce to public WSS trackers in parallel after SRIFT's own (a transfer never waits for them). Off by default: WebTorrent seeds file bytes as they are, so a public tracker's operator could join the swarm and fetch them, while SRIFT's own tracker only serves the session's members. - HTTPS session stream for networks that block WebSockets. After two WebSocket attempts that never open, the page switches to
/v1/stream/http/*(sequence-numbered, acknowledged batches plus a long poll) with the same server behaviour, routed to the session's home instance. - Participant-only keys for traffic through the server. The chat copy for CLI/agent members (
pk1) and WebSocket-relayed file chunks (pgcm1) are sealed with ECDH P-256 keys generated on the devices (HKDF-SHA256, AES-256-GCM, a fresh key pair per transfer), exchanged over the session socket (e2e_key,relayPkinfile_accept). Older clients keep the session-key format. - CLI HTTPS-stream fallback. When WebSockets do not hold on a network (two attempts never open or are cut before authentication), the daemon switches signaling to the HTTPS session stream through its proxy-aware client, and tries a WebSocket again every 10 minutes.
SRIFT_SIGNALING=httpforces it. - Retention everywhere on the client. CLI link history (
SRIFT_HISTORY_DAYS, default 30, expired links dropped at once), daemon log age limit (7 days on top of the 2 × 1 MB cap), crash leftovers in~/.srift/tmp, staged outbox copies (30 days, swept on every run), abandonedsrift getpartials (7 days), Peerknock queued messages and old groups, and in the browser a sweep on every page that erases sessions idle for a day (keys, chat copy, activity log, mesh store, service-worker state).
Changed
- Audio conferences no longer use LiveKit. Browsers in a session form a peer mesh (
lib/p2p/): every pair talks over a direct WebRTC connection (DTLS-SRTP, keys generated on the two devices), up to 10 participants in a full mesh, beyond that on a bounded-degree overlay (at most 8 links per browser). A pair that cannot connect directly hears each other as Opus frames sealed with the speaker's sender key and carried by other participants, or relayed by the session WebSocket as a last resort. The server keeps no room, roster or media token;LIVEKIT_*variables,/debug/audioand thelivekit/healthzstep are gone. Older clients sendingaudio:*get a "reload to update" reply. - Browser chat goes peer to peer. Messages are signed by their author (ECDSA P-256), sealed with the sender's key (AES-256-GCM, handed to each member under an ECDH P-256 pair key, rotated when someone leaves) and gossiped over data channels, so a relaying member can neither read nor forge them. Late joiners get earlier messages from peers. A copy still goes over the session socket, under the same id, only while CLI/agent members (which do not speak the mesh) are present, so
srift_send_chat/srift_chat_historykeep working. - WebRTC signalling between browsers runs through the mesh once a browser has one peer link; the server (
mesh_relay, forward-only, never parsed or stored) is used for first contact and for members no peer path reaches. - Chat history, identity keys and peer key pins stay on the device (IndexedDB) and are erased when the session ends, the user leaves or is removed.
- The server keeps no database: sessions and members live in RAM only (
lib/server/ram-store.mjs), deleted with the session. Chat is no longer written anywhere (it was stored as ciphertext before); only message ids are kept, for reply validation. - Server instances find each other over an authenticated instance mesh (
lib/mesh/), dialling the service's own address, with nothing to configure: the key is derived fromENC_KEY, and on Cloud Run the address is the deterministic run.app URL. It replaces PostgresLISTEN/NOTIFY. - Each session has one home instance. WebSockets, joins, session-info/validate,
/d/downloads, hosted MCP calls, Peerknock long-polls and tracker sockets (/v1/peers?s=<session>) that land on another instance are tunnelled or forwarded there, so every member meets on one instance. - Each home keeps a RAM copy of its sessions on a sibling instance: if the home dies, the copy takes over and members reconnect within seconds; on shutdown sessions are handed over first. An instance the platform stops sending work to hands its quiet sessions and idle clients to busy instances and leaves the mesh, so it can be retired.
/readyzno longer checks a database;/healthzreportssessionsandclusterinstead ofdatabase.- Every SDK (Python, Node, Go, Rust, Java, .NET, PHP, Ruby, Bash, PowerShell) has
diagnose()for the daemon's/v1/diagnetwork diagnosis, matching MCPsrift_net_diagnose. - Dependencies updated to their latest compatible versions; unused packages (
googleapis, the Babel toolchain,core-js,date-fns,react-day-picker,emoji-picker-react,concurrently,ignore-loader) removed. /terms,/tosand/terms-of-serviceredirect to the Terms of Service./.well-known/agent.jsonis labelled a discovery card; per-agent A2Amessage/sendlives at/a/<address>./agents.mdand/ai-instructions.mdare served frompublic/like/AGENTS.md.- CLI help lists Peerknock (
srift pk) and the reallinks addflags; the no-op--no-daemonflag is gone from the help;srift infostates the current key exchange (ECDH P-256 + HKDF-SHA256). - Site, privacy policy and agent docs corrected against the code: real browser-storage keys, real TURN providers, TLS 1.2+ (1.3 preferred), WebTorrent opt-in, relayed (not peer-to-peer) quick-share links, full Peerknock command and environment-variable reference in AGENTS.md.
- SRIFT is presented as what it is: an open-source project (MIT, "The SRIFT contributors"), not a company. Terms, privacy, about page, structured data and discovery files updated; contacts consolidated on support@sripto.tech.
- Brand written as SRIFT everywhere; "military-grade AES-256-GCM" and "decentralized" wording allowed and used; the about-page comparison compares technology instead of storage caps.
Fixed
- Session mesh: a link whose connection is
disconnectedis bypassed at once (traffic moves to peers or the server) instead of receiving messages until ICE recovers; a reliable send that fails is rerouted rather than dropped. - The quoted text of a reply no longer travels to the server in the chat copy.
- A chat message that cannot be decrypted is no longer shown as ciphertext.
- Quick-share
/d/links, relayed file chunks, join approvals, hosted MCP sessions, the WebTorrent tracker and Peerknock polls/acks no longer break when requests land on different instances. /validate-sessionno longer reports a live session as inactive (and wipes it in the browser) when asked on another instance.- A join request made while the host is reconnecting now reaches the host when it comes back.
- The CLI daemon's re-authentication after approval no longer closes its own socket (it had to reconnect, which slowed joins).
- Peerknock: a listener connection on another instance (e.g.
srift pk filewaiting for the answer) now receives its deliveries. - Instance mesh: a reply or a forwarded request that found no route while links were rotating was dropped, so the caller waited the full 10 s timeout. Replies and forwarded frames now wait briefly for a route, like requests already did.
- CLI relay uploads no longer hang when the signaling socket drops mid-transfer: an unacknowledged chunk is sent again after 15 s and right after a reconnect; receivers de-duplicate; a lost
file_completeno longer leaves a finished download unassembled. - CLI daemon detects half-open signaling sockets (no heartbeat ack for 75 s) and reconnects.
- A CLI guest no longer shows up to the host as "CLI-Agent" when its join request goes over the socket: the
--usernamegiven tosession joinis used. - Browser: reconnects without an attempt limit and at once when the device comes back online; frames sent while reconnecting are queued and delivered; relay transfers re-send unacknowledged chunks instead of failing after 30 s; a WebRTC file link that blips
disconnectedgets 5 s to recover; the HTTPS stream is also used when WebSockets are cut before authentication, and the page moves back to WebSockets when they work again. /d/downloads whose bytes stop flowing for 60 s are closed so clients resume with a Range request instead of hanging.- Browser TURN credentials that arrive after the 1.5 s wait are cached for the next link and every ICE restart instead of being discarded.
- The GitHub Action no longer leaves
srift-share-result.json(which held the#k=key) orerr.login the workspace, and masks the URL before writing outputs.
Security
- The CLI sends every file size over the end-to-end sealed relay by default (WebTorrent is now opt-in). Files over 10 MB used to go to WebTorrent, where a CLI-to-CLI transfer could stall with no fallback, the bytes were not end-to-end encrypted, and the client joined the public DHT. WebTorrent, when requested, no longer joins the public BitTorrent DHT or local service discovery, and SRIFT torrents are private (BEP 27). Before, a transfer's infohash was announced to the public DHT while the file was seeded as is, so a stranger who saw the hash could have downloaded it. Peers now come only from SRIFT's session-scoped tracker.
- Server logs carry no usernames; user ids and session codes are replaced by keyed hashes that are stable within one instance's logs and cannot be reversed (
lib/server/log-privacy.mjs), including libraryconsoleoutput. - Server logs no longer contain client IP addresses: IPv4 and IPv6 addresses are replaced by keyed hashes like user ids and session codes (the abuse warnings printed them in clear).
[4.2.0] — 2026-09-26
Peerknock across every server instance, one live holder per @handle, and parallel live calls.
Added
- Live calls (default for
srift pk call): an agent can hold as many calls at once as it can handle: no built-in cap; it sets its own withsrift pk calls limit <n|off>(MCP:srift_pk_callsactionlimit); beyond it callers hear "busy". Finished calls follow the agent's own retention (srift pk prune). Callees accept, decline with a reason or say busy (srift pk reject <callId> --busy). Several targets = a conference;srift pk calls addrings more people mid-call;srift pk calls mergepulls everyone into one call or into a group chat (--group / --new-group);calls send|file|hangup|history. Group calls are live conferences.--sessionkeeps the previous SRIFT-session calls. - Peerknock MCP: new
srift_pk_callstool (25 tools);srift_pk_calltakesothersfor conferences,srift_pk_accept_calltakesbusy. Server instructions now tell agents to ask other agents for help instead of guessing, answer knocks and run conversations in parallel. The coresrift mcppoints agents to Peerknock. - Peerknock search: a statement of need or a name finds agents with similar wording, related words ("translate" ~ "interpret", "invoice" ~ "billing"), typos (1 edit for 5+ letters, 2 for 8+) and nearby usernames ("@datahelpr" → "@data-helper"); exact words still rank first. Default 20 results (max 50).
- Descriptions over 160 characters are refused with a clear message instead of being cut silently (
srift pk host,describe,srift_pk_announce). - Knocks say who and why:
from(username, description, skills — checked against the signed card) +need+note(srift pk knock <to> --need "…" ["note"], MCPsrift_pk_knockneed). Answers carrystatusaccepted | rejected | busy, the answering agent's ownreason, its username and description, andretryAfterSecwhen busy (srift pk answer <id> accept|reject|busy "reason" [--retry-after 10m], MCPsrift_pk_answer_knockbusy/retryAfterSec); the knocker decides to retry or move on.srift pk knockexits 4 on busy. No canned reasons: automatic answers state the agent's real situation; decide hooks may returnreason,busy,retryAfterSec. - Activity log: the node writes its own
node.log(metadata only, never message text), capped at ~0.5 MB (node.log+node.log.1) and keptlogRetentionDays(default 7,srift pk retention … --logs <d>). Agents read it withsrift pk logsor the MCP resourcesrift://peerknock/log. Detached nodes no longer pipe raw output into an ever-growing file; the daemon log now rotates while running (1 MB + one previous generation). - Retention: sent knocks (connection history) and keys of agents met are also capped by
retentionDays. - CLI:
--need,--retry-after,--logs,--tail,--groupand--new-grouptake values (their values were previously read as positional arguments, e.g.calls merge … --group g). - New Agent Skill
peerknock(/.well-known/agent-skills/peerknock/SKILL.md, also over MCP skills/list).
Fixed
- Peerknock relay: several relay instances behind one URL (Cloud Run autoscaling) now act as one relay. Presence, live cards, search and message/file delivery are synced over a transient Postgres LISTEN/NOTIFY bus with fragmentation; nothing is written to a table. Previously agents on different instances could not find or reach each other.
- Handles: a bare @name has ONE live holder across the network (the agent that has claimed it longest among those online). Others claiming it are told (handle.held: false) and stay reachable by their ~tag; the name frees up when the holder goes offline.
srift pk id handle @namerefuses a name another online agent holds (--anyway to keep it as a display name). - Docs fixed:
srift install-mcpprints config snippets (only--autowrites Claude Desktop config); the MCP protocol version is 2026-07-28 (older versions still accepted); removed a docker command that did not run the CLI daemon.
[4.1.0] — 2026-09-26
Release from a full production audit (srift.app, npm, PyPI, MCP registry, every CLI command, MCP tool, SDK and browser flow). New: session participants, joinUrl, Go NewSession, the Python SDK inside pip install srift, structured MCP outputs, npm run e2e:matrix. No breaking changes.
Fixed
- Download links over 32 MiB failed on srift.app (HTTP 500). Cloud Run rejects HTTP/1 responses that declare more than 32 MiB; large bodies are now streamed chunked there, and the size travels in HEAD's
Content-Lengthand a newX-SRIFT-Sizeheader (SRIFT_MAX_SIZED_BODYoverrides the limit). - A deleted or changed shared file made
srift getretry for ~50 s, then print Cloudflare's HTML error page. The server now answers424with anX-SRIFT-Error: sender-fileheader (Cloudflare replaced the old502body), andsrift getfails at once with the real reason.srift getnever echoes proxy HTML pages. - Files between the CLI/MCP daemon and browsers in a session never arrived. The daemon sent base64 chunks the web app ignores, so agent → human transfers hung. The daemon now negotiates the web app's end-to-end encrypted relay format (
relayEnc: 'sgcm1', AES-256-GCM per chunk) in both directions and keeps the old format for older daemons; the web app also accepts chunks from 4.0.0 CLIs. Session files relayed between daemons are now sealed too. - Chat and file offers sent right after a join was approved were silently dropped while
success: truewas returned. The daemon now waits (up to 8 s) until its session connection is authenticated, and reports "waiting for host approval" for an unapproved guest. - Invite links from agents opened an empty join form.
/join-sessionnow reads?id=(used by the CLI, daemon, MCP tools and SDKs) as well as?sessionId=. - Peerknock: a known contact could not be reached by
@handleor@handle~tagunless they were publicly discoverable. Contacts are now resolved first; the~tagis checked against the contact's key-derived address. /.windsurfrulesreturned 500 (listed in the sitemap). It now has an explicit route.- Python SDK sends a
srift-python-sdk/<version>User-Agent; Python's defaulturllibagent is refused by srift.app's CDN. - Session daemon reconnected every 3 seconds forever after its socket was replaced (e.g.
srift session startafter a quick-share): the old socket's close handler scheduled a reconnect that closed the new socket. Each reconnect killed in-flight transfers. Events from replaced sockets are now ignored (daemon and Peerknock relay client). - Quick-share links broke when the same daemon started a session. Links now carry a server-issued claim, so the daemon keeps the same URLs across sessions.
- A kicked guest's daemon kept reconnecting;
kicked_from_sessionnow ends the session locally, and chat/send report why (410). - Hosts could not kick anyone in practice: user ids were only visible on the guest.
/status,srift session statusandsrift_session_statusnow listparticipantswithuserId;/session/approvereturns once the guest is in the room (joined,userId). - Queued Peerknock messages waited up to 60 s when the recipient came online just after the message was queued by another CLI process; targets that are already online are flushed at once.
srift monitor <fileId>hung forever for finished or unknown transfers;srift history --jsonignored--limit.srift_start_sessionandsrift session startreturned a hard-coded srift.app join link on self-hosted servers; they now return the realjoinUrl.- MCP tools return structured output matching their declared
outputSchema(thesrift_session_statusschema is corrected;srift_list_transfers,srift_chat_history,srift_send_file,srift_start_sessionand the hosted tools gained structured results). - SDKs: Rust
quick_sharefailed to deserialize (required aprotocolfield the daemon doesn't send); Go gainedNewSession(returns the session id and join URL) andParticipants; the Bash SDK built invalid JSON for Windows paths and quotes and itssrift_alivebroke on Windows curl. - Web app: approve / reject / remove buttons had no accessible names (screen readers and AI browser agents couldn't use them).
- In-memory test store:
DELETEstatements were ignored and guests were stored as hosts.
Packaging
server.json:mcpmoved fromruntimeArgumentstopackageArguments; the PyPI package is listed (its README carries themcp-namemarker the registry requires).integrations/: the Docker, Kubernetes, Cloud Run, Lambda, Workers, Replit and GitHub Actions recipes could not work (the daemon is loopback-only) and two exposed the unauthenticated daemon publicly; they now use a shared-localhost sidecar or the CLI in the agent's container.
SEO and AI readability
- Removed a fabricated
aggregateRating, 20 hreflang tags pointing at the same English page, conflicting hand-written OpenGraph tags, superlative "better than …" meta tags and fake response headers; the FAQ/HowTo JSON-LD is on the homepage only; entity@ids are shared across pages;dateModifiedis the release date. - Hidden (
sr-only) crawler-only content is now visible (collapsed sections) or removed. - Real 1200×630
og-image.pngand a 512×512 logo; robots.txt rules now apply to every crawler; sitemap uses real dates and lists pages plus the AI reference files; RSS is built from the changelog; the 404 page isnoindex. llms.txtrewritten in the llmstxt.org format with quick answers first; AGENTS.md, ai-instructions.md, llms-full.txt and the /ai-agents page now document the real error codes (no JSON-RPC -32001…-32004, noGET /events), the 4.1.0 API fields, the real CLI help and Peerknock.- OpenAPI: every daemon endpoint, response schemas and
operationIds (for GPT Actions and tool generators).
Docs
- Python: one package.
pip install sriftnow includes the Python SDK (from srift import Srift) next to thesriftcommand; the never-publishedsrift-sdkpackage definition is retired. Removed the install command for the unpublished Node SDK (npm install srift). .cursorrulespointed at binaries under/dl/2.2.0/(404); SDK package manifests said3.0.0. Both are now kept current byscripts/sync-version.mjs.llms-full.txtreferenced a nonexistentsrift tunnelcommand; added424to the HTTP status tables;docs/DISTRIBUTION.mdrefreshed to the live state.- Accurate security wording everywhere. Session keys derive from the session ID plus an optional room secret, so pages no longer say the server "cannot decrypt" default browser sessions; they point to
--room-secretfor participant-only keys. Plain quick-share links are described as TLS in transit (E2EE with--encrypt). Removed forward-secrecy, "no message storage", HIPAA/SOC 2 "compliant", RSA-4096 and nonexistent-feature claims (video calls, recordings, fraud detection, ghost messaging); audio is LiveKit (DTLS-SRTP); WebTorrent is opt-in; retention is host close or 7 days of inactivity.security.txtis now a plain RFC 9116 file andhumans.txtlists real facts instead of keywords. - Landing pages,
/ai-agents, READMEs,.cursorrules/.windsurfrules/GEMINI.md:srift links(notpubshare), fulldoctorandquick-shareflags, fullsrift_quick_shareparameters, the real SSE event list,503then404for offline senders, Python 3.9+ for pip, loopback-only SDK runtimes, and the hosted MCP's 9-tool limit.
Tests
- New e2e group
session: host and guest daemons join, approve, chat both ways and send files in both directions (SHA-256 checked); it also fails if an idle host keeps reconnecting. - New
npm run e2e:matrix: every CLI command/flag, every Peerknock command and MCP tool (two federated relays), and all local + hosted MCP tools with output-schema validation. - Regression tests for each fix above.
[4.0.0] — 2026-09-25
Major release: adds Peerknock (agent-to-agent communication) and removes all server-side storage (see Removed).
Added
-
srift get <link|token>command (aliassrift download): Download any SRIFT link using Node's HTTP stack. Works wherecurlfails in sandboxed environments. Supports--password,--force,--json, output redirection (-o <file|dir|->), HTTP Range resume, and end-to-end encrypted links with fragment keys. No install needed:npx -y srift-transfer get "<url>". -
Encryption for quick-share links: Fragment-key E2EE (SRE1 format) with AES-256-GCM, opt-in via
--encrypt; the key stays in the#k=URL fragment and the browser decrypts locally. Password second-factor with--password(PBKDF2-SHA256 100,000 iterations). -
Folder/recursive sharing:
srift quick-share ./path/to/dir— packs as.tar.gz, excludes.gitandnode_modules, supports--excludeglobs. -
Stdin input:
cat x | srift quick-share - --filename file.txtfor piping data without temp files. -
QR code output:
srift quick-share <file> --qrprints a scannable QR code for the download link. -
Transfer history:
srift historylists links created locally (stored only on your machine in~/.srift/history.jsonl, file mode 0600; entries include#k=keys, so treat the file as secret;srift history --clearwipes it). Disable withSRIFT_NO_HISTORY=1. -
Shell completion:
srift completion bash|zsh|fish|powershellfor CLI autocompletion. -
srift doctorhealth check: Comprehensive transport diagnostics (HTTPS, daemon, WebSocket, UDP, peer discovery, sandbox detection) with exact fix suggestions and meaningful exit codes (0=ok, 1=degraded, 2=blocked). Available as MCP toolsrift_net_diagnoseand daemon endpointGET /v1/diag. -
Proxy and custom-CA support: Honors
HTTPS_PROXY,HTTP_PROXY,SOCKS5,NO_PROXY,NODE_EXTRA_CA_CERTS— works in corporate and sandbox networks. ExplicitNODE_USE_ENV_PROXY=1for Node ≥24 (opt-in for older Node versions). -
MCP protocol 2026-07-28:
server/discoverimplemented, Skills over MCP (skills/list,skills/get,skill://resources with sha256 digests), expanded tool set withsrift_net_diagnose. -
Neutral endpoint paths:
/v1/peers,/v1/streamalongside legacy/announce,/tracker,/wsfor networks that blocklist P2P keywords. Tracker selection viaSRIFT_EXTRA_TRACKERS. -
Embedded daemon for sandboxes and datacenter agents: when the background daemon cannot start (local servers forbidden, detached spawn or loopback blocked),
srift quick-shareandsrift mcprun the daemon inside their own process with no port — only outbound HTTPS/WSS on 443.--foregroundforces it; the MCP server switches automatically if the daemon becomes unreachable. -
srift quick-share --wait [--wait-timeout <dur>]blocks until the link is downloaded (exit 3 on timeout);--keep-alivekeeps serving until expiry. -
Multi-file / parallel sharing:
srift quick-share <path> <path> …bundles files and folders into one.tar.gzlink (colliding names become "name (2).ext",.gitandnode_modulesexcluded). Params:--bundle-name <name>,--exclude <glob>(repeatable). Use--separatefor one link per path, created in parallel. All flags apply to every link:--encrypt,--password,--ttl,--once,--max-downloads,--wait,--keep-alive,--foreground. -
Parallel downloads:
srift get <link> <link> … [-o <dir>] [--concurrency N](default 4, max 16) downloads multiple links in parallel, decrypting#k=links, and exits with code 1 if any failed. -
MCP multi-file support:
srift_quick_sharenow acceptsfilePaths: string[](up to 500) orfilePath, plusbundle(default: true = one .tar.gz; false = one link per path),bundleName,exclude: string[]. ReturnsdownloadUrl/fileName/fileSize/encrypted/bundle/files(single bundle) or{ links: [], errors: [] }(separate mode; per-path failures don't halt others). -
REST quick-share:
POST /quick-shareacceptsfilePathsarray +bundle,bundleName,exclude[]params; bundle responses addbundle: trueandfiles(count packed);bundle: falseresponses returnlinks[]pluserrors[]for per-path failures. MCP tools can now returnstructuredContent(required by the spec when a tool declares anoutputSchema). -
Peerknock (agent-to-agent communication):
srift peerknock(aliassrift pk) gives every agent a permanent address (srift:XXXX-…, the hash of its own Ed25519 key) and a@name~xxxxxxxxtag. Includes live search of agents online right now, ranked by their self-written one-line descriptions (srift pk search,--watchnotifications); knocks answered with accept/reject and a note;srift pk connect "<need>"(search → knock → first message, moving to the next agent on rejection); E2EE chat (X25519 + HKDF-SHA256 + AES-256-GCM, Ed25519-signed); native chunked file transfer (SHA-256 verified); calls over SRIFT sessions; admin-signed group chats; owner proofs (@owner/agent); invite links;name@domainvia/.well-known/srift; A2A Agent Card and did:wba export. Separate MCP serversrift peerknock mcpwith 24srift_pk_*tools (the coresrift mcpstays at 15). -
Peerknock relay in
server.mjs(/api/pk/*, WebSocket/pk,/a/:address): RAM only, no database, forwards only to online agents (delivered / offline / unconfirmed), HTTP long-poll fallback for proxies and sandboxes, federation with peer relays (SRIFT_PK_PEERS), per-IP limits, host-bound signed logins. Self-host withsrift pk relay serve [--peers …]. -
Peerknock local data controls:
--ephemeral/SRIFT_PK_EPHEMERAL=1(RAM only, temp files wiped on exit), retention (30 days default),srift pk prune,srift pk wipe, log rotation, optional passphrase-encrypted identity (SRIFT_PK_PASSPHRASE). -
npm run e2e:peerknock-sandbox: Linux network-namespace test with proxy-only egress.
Changed
- Download guidance: All documentation now recommends
srift get <url>first for cross-platform compatibility, thenwget, then PowerShell, thencurlas last resort. - Crypto transparency: Quick-share links are end-to-end encrypted when created with
--encrypt. Session transfers and chat remain end-to-end encrypted; unencrypted relay links have zero retention (never stored). Updated AGENTS.md, CLAUDE.md, README.md with accurate carve-outs. - Agent Skills naming:
/.well-known/agent-skills/is now documented as a SRIFT convention (proposed, not a standards committee artifact). Formerly mislabeled "AGNTCY" throughout.
Removed
- All centralized server-side storage (owner decision, 2026-09-24): push mode (the
--modeflag), the/v1/blobAPI andDELETE /v1/admin/blob, transfer.sh-stylePUT /:filenameuploads, the blob store (Postgres / GCS / filesystem) and its quotas,SRIFT_BLOB_BUCKET,SRIFT_ADMIN_TOKEN,SRIFT_ALLOW_PLAINTEXT_PUSH,SRIFT_UPLOAD_INFLIGHT_BYTES. Quick-share links are relay-only: the sender's daemon streams bytes on demand and nothing is stored, so the link works only while that daemon runs. - Server-minted Cloudflare TURN credentials (
GET /v1/turn,CF_TURN_KEY_ID,CF_TURN_KEY_API_TOKEN,CF_TURN_TTL_SECONDS). ICE uses public STUN only; self-hosters can supply their own list viaNEXT_PUBLIC_ICE_SERVERS. - Hosted A2A endpoint (
https://srift.app/a2a/v1),/.well-known/agent-card.json, and the secure-artifact-transfer extension. Agent-to-agent transfer uses MCP (srift mcp), the CLI, the SDKs, and E2EE P2P sessions. - Daemon origin allow-list (
SRIFT_DAEMON_ALLOWED_ORIGINS): the local daemon answers CORS for all origins (Access-Control-Allow-Origin: *).
Fixed
- The CLI could hang forever when something else on the daemon port accepted connections without answering; daemon probes and the startup wait are now bounded.
- Curl compatibility in sandboxes:
curl: (43)errors in Claude Code / E2B / Modal sandboxes no longer block all downloads;srift getprovides a working alternative. - Proxy passthrough in corporate networks: HTTP(S) proxies, SOCKS5, TLS-inspecting proxies with custom CA certificates now fully supported.
- Security: the local daemon wrote every decrypted session chat message to
~/.srift/daemon.log. It now logs only the sender and message length.daemon.logis created owner-only (0600) and rotated at 5 MB. scripts/sync-version.mjsnow also syncs the PyPI package, the Python SDK, the.mcpbmanifest and the Homebrew / Scoop / AUR / winget manifests, which previously lagged one version behind (and blocked the PyPI build).- Agent Skills files are pinned to LF line endings, so their published SHA-256 digests are identical on every OS.
- Dependencies updated to clear all
npm auditadvisories.
Deprecated
- Static binaries are still provided, but PyPI, Homebrew, Scoop, winget, AUR, GitHub Action, and
.mcpbbundles are coming soon (not yet published).
Documentation
- New spec
docs/quick-share-e2ee.mddescribing SRE1 encryption format, key derivation, nonce construction, and threat model. - Updated AGENTS.md §11 (Crypto) with quick-share carve-out.
- Updated CLAUDE.md critical directive with
srift getand--encryptguidance. - Updated README.md §Security with the E2EE fragment-key explanation.
[3.0.0] — 2026-09-11
Version realignment and version-integrity release. Consolidates the never-published 2.2.13–2.2.16 source bumps into a single published 3.0.0 across npm, the Official MCP Registry, the CLI, the local daemon, the hosted MCP endpoint and all ten SDKs.
Not breaking despite the major bump. There are no API, wire-protocol or
crypto changes; clients built against 2.x keep working, which is why
minSupported stays at 2.0.0. The major number marks the end of the
2.2.x drift rather than an incompatibility.
Fixed — version sync was silently broken
sync-version.mjs reported "all references match" while three runtime
surfaces were stale. The cli/mcp.ts pattern was built from a non-raw
template literal, so \s collapsed to s and to a real newline; the
regex could never match. Because the script only compared text before and
after, a pattern matching nothing was indistinguishable from a file already
correct — so it was skipped silently and permanently.
- MCP
serverInfo.versionwas stuck at2.2.15. - The local daemon reported
2.1.8on/healthand/openapi.json, and2.0.0on its own MCP server-card — whilecompat.jsonadvertised2.2.16. Its four hardcoded versions now reference one synced constant. - The script now fails loudly when any pattern matches nothing.
Fixed — a banned claim was live in production
The forbidden number-one ranking claim was being served from /feed.xml (RSS
heading + two descriptions) and /.well-known/security.txt (two banner lines).
The audit only ever read robots.txt, so it reported a 100% pass throughout.
Detection also matched CSS hex colours — the old regex only excluded a following
digit, so hex values like 1a1a2e and 10B981 in the OpenGraph and Twitter
image routes were flagged — and now requires a non-word character after the
marker.
The audit now scans every served surface: all of public/, app/**/route.ts,
app/**/*.tsx, and the repo-root files that server.mjs serves publicly
(CHANGELOG.md, AGENTS.md, ai-instructions.md, .cursorrules).
Added — every SDK has a synced version
All ten SDKs now expose exactly one version constant, idiomatic per language:
Go Version, Java VERSION, .NET Version, PHP VERSION, Ruby VERSION,
Bash SRIFT_SDK_VERSION, PowerShell $Script:SriftSdkVersion, Rust
pub const VERSION, plus the pre-existing Node VERSION and Python
__version__. Previously only Node, Python and Rust carried a version at all,
and all three were stranded at 2.0.0. The Node and Python MCP handshakes now
send the constant rather than a literal.
sync-version.mjs covers all of them (plus cli/daemon.{ts,js}), so one
package.json bump propagates to 40 files.
Fixed — miscellaneous
sdk/rust/Cargo.tomlrepositorypointed at the non-existentsripto/srift-website; nowsrivardhan113/SRIFT-Open_Source.- Release
kindwas hardcoded'minor'and would have advertised this major release as minor.kindandpublishedAtin/cli/version.jsonand/sdk/*/version.jsonare now derived from the version and the changelog.
Removed
sdk/python/__pycache__/srift.cpython-313.pyc, a compiled artifact committed to the repository.__pycache__/and*.py[cod]are now gitignored.
[2.2.2] — 2026-06-30
Fixed — daemon-side companion to the 2.2.1 cap-exhausted-410 fix
Caught in real production testing of the 2.2.1 deploy: the second GET on
a --once link still returned 404, not 410. Root cause: while 2.2.1 fixed
the server (made the token-release lazy inside _pubResolveDownload),
the daemon was still proactively sending pubshare_unregister to the
signaler the instant the cap was hit. The signaler dutifully deleted the
token from pubsharesByToken, so the next request hit the "token unknown"
branch (404), bypassing the brand-new "exhausted" branch (410). Now the
daemon keeps the entry locally and lets the server's lazy release fire.
Bonus fix from the same audit: reregisterPubsharesAfterReconnect() was
replaying exhausted entries with remainingMax = max(1, 0) = 1, which
would reset the cap and effectively allow one more download per WS
reconnect — violating the --max-downloads contract. Now skips
exhausted entries entirely on reconnect.
Verified end-to-end against production
- ✅
srift quick-share --once→ first GET 200 + byte-perfect MD5 match - ✅ Second GET → HTTP/1.1 410 Gone with body
SRIFT: this download link has reached its download limit. - ✅ HEAD
/d/<token>→ 200 with all metadata headers:Content-Length,Content-Disposition,Accept-Ranges,Cache-Control: no-store,X-SRIFT-Downloads: 0/unlimited,X-SRIFT-Expires: never - ✅ Range
bytes=0-99→ 206,content-range: bytes 0-99/102400, 100 bytes exactly - ✅ Range
bytes=51200-→ 206,content-range: bytes 51200-102399/102400 - ✅
srift pubshare listcounters advance correctly after each download - ✅
srift pubshare revoke <token>→ 404 immediately - ✅ Unknown token → 404 with friendly body
- ✅ Daemon stop → all tokens released, recipients see 404
- ✅ Reconnect-replay no longer resets exhausted counters
[2.2.1] — 2026-06-30
Fixed — bugs surfaced by post-deploy production testing
HEAD /d/<token>returned 500 Internal Server Error. TheX-SRIFT-Downloadsheader contained the U+221E∞infinity glyph when the link was uncapped, which fails Node's ISO-8859-1 header validation and throws. Changed the unlimited sentinel to the ASCII stringunlimited. HEAD now returns proper 200 with the metadata headers (Content-Length,Content-Disposition,X-SRIFT-Downloads,X-SRIFT-Expires).- Exhausted links returned 404 Not Found instead of 410 Gone. The
server was releasing the token immediately when
--max-downloadswas hit (insidepubshare_end), so the next GET hit the "token unknown" branch instead of the "reached its download limit" branch. Now the release happens lazily inside_pubResolveDownload()on the next request, which gives the recipient the proper 410 + "this link has reached its download limit" message — distinguishable from a typo.
Verified end-to-end against production (real two-peer test)
- ✅
srift quick-share <file>→ realhttps://srift.app/d/<token>URL - ✅
--once,--ttl 5m,--max-downloads Nall enforced server-side - ✅ Full GET → byte-for-byte MD5 match
- ✅ Range
bytes=0-99→ 206 Partial Content, 100 bytes exactly - ✅ Range
bytes=51200-→ 206,Content-Range: bytes 51200-102399/102400 - ✅
srift pubshare listshows counters advancing after each download - ✅
srift pubshare revoke <token>→ next GET returns 404 instantly - ✅ Sender's daemon stopped → all tokens released, next GET returns 404
- ✅ Unknown token → 404 with friendly message body
- ✅ HEAD probe (after the fix in this release) → 200 with metadata headers
Docs — comprehensive ai-agents page rewrite
- New flagship "Public Download Links" section on
/ai-agents, surfaced as the first sidebar entry. Covers: sender + recipient CLI examples, full lifetime model table, concurrency model + throughput numbers, HTTP status code contract, per-audience usage (AI agents / humans / scrapers), a "how a byte travels" architecture diagram, and the canonical endpoint reference. - Updated the troubleshooting table with realistic failure modes for the new flow (404 / 410 / 503 / signaler-version-skew).
2.2.0 — 2026-06-30
TL;DR
srift quick-share <file> now returns a real public download URL —
https://srift.app/d/<token> — that the recipient can fetch with any HTTP
client: a browser, curl, wget, PowerShell iwr, mobile Safari, a CI
job, an embedded device. They do not need SRIFT installed. Plus
server-enforced sharing limits (--max-downloads, --ttl, --once),
link management (srift pubshare list|add|revoke), HEAD support, Range
resume, and token survival across transient signaler reconnects.
Added — public download tunnel
GET /d/<token>andHEAD /d/<token>on srift.app — streams the file straight from the seeder's daemon through the signaler out as a standard HTTP response.Content-Disposition: attachment; filename="…",Accept-Ranges: bytes,Cache-Control: no-store. Recipients no longer get stuck on the join-session approval flow — quick-share bypasses it entirely.srift quick-share --max-downloads N— server-enforced cap. Only completed full downloads count; failed/cancelled and Range-resume requests don't burn slots.srift quick-share --ttl 30s|15m|2h|1d— server-enforced expiry.srift quick-share --once— shorthand for--max-downloads 1.srift pubshare list— show all active public links with file size, download count vs cap, expiry, and the underlying token.srift pubshare add <file> [--once|--ttl|--max-downloads]— mint additional links in the current session.srift pubshare revoke <token>— invalidate a link immediately.- Daemon re-registers all active pubshares with the same tokens after WS reconnect, so URLs survive transient network drops.
- Concurrent downloads are unlimited — each recipient gets an independent parallel stream. Sender's bandwidth is the natural limit.
Lifetime model
| Event | Effect |
|---|---|
| Daemon stops or disconnects | All tokens released; /d/<token> returns 404 |
| Daemon WS reconnects | Daemon re-registers, same tokens, links keep working |
| TTL elapsed | Server returns 410 Gone, token auto-released |
maxDownloads reached | Server returns 410, token auto-released |
| Recipient cancels mid-download | Stream aborts cleanly; slot not consumed |
srift pubshare revoke <token> | Immediate invalidation |
Changed — CLI output trimmed, internal transport details hidden
srift quick-shareoutput is now just the download URL, file size, current limits, and copy-paste-readycurl/wgetexamples. No moreSession ID,File ID, orProtocol: websocketnoise — those were internals and confusing for end users.srift list,srift send, and every MCP tool description no longer surface the internalwebsocketvswebtorrentprotocol label. Transport selection is fully automatic.srift sendusage simplified tosrift send <filepath> [--json].- CLI is now honest about which mode it's in: shows
[SRIFT] Quick share ready.for the direct-download path, and[SRIFT] Quick share ready (legacy session-join mode).with a clear explanation when the signaler hasn't yet picked up the new release.
New WebSocket signaling verbs (server.mjs ↔ daemon)
pubshare_register/pubshare_register_ack— daemon advertises a file (with optionalmaxDownloads,expiresAt,preferTokenfor resume), server mints/refreshes the token and returnsdownloadUrl.pubshare_pull— server asks daemon to stream a byte range for an in-flight HTTP request. CarriesisFullDownloadso partial Range-resume requests don't count towardmaxDownloads.pubshare_chunk— ordered 64KB chunks from daemon → server → HTTP body. Bypasses the per-connection flood limiter so throughput isn't capped at 20 msg/s.pubshare_end/pubshare_cancel/pubshare_unregister— lifecycle.
New daemon REST endpoints
POST /pubshare— register a file in the current session, returnsdownloadUrl.GET /pubshare/list— every active public link withdownloadCount,maxDownloads,expiresAt.POST /pubshare/revoke— invalidate by token.
Fixed
- Recipients no longer get stuck on the join-session page waiting for
a host approval that the CLI quick-share daemon never sends. The new
/d/<token>path bypasses the session-join UI entirely. - When the signaler doesn't yet support pubshare, the daemon now cleans
up the half-baked local registration (previously left orphan entries
in
/pubshare/list). - Hidden the internal transport label (websocket vs webtorrent) from every user-facing output. Transport selection remains fully automatic.
Deprecated
srift send --protocol webtorrent|websocket— the flag still works for debugging but is no longer documented in--helpor tool descriptions.
Docs
app/ai-agents/page.tsx,public/llms.txt,public/llms-full.txt,public/openapi.json,public/changelog.json,public/compat.json,public/.well-known/mcp/server-card.json,public/.well-known/agent.json,public/.well-known/ai-plugin.json,AGENTS.md,ai-instructions.md,.cursorrules,README.md— all updated with the new download URL pattern, lifetime model, sharing flags, and v2.2.0 binary URLs.
[2.1.11] — 2026-06-30
Fixed — sessions leaked in the cloud (found via real end-to-end prod testing)
session closenever told the server to tear down the session. The daemon only dropped its WebSocket, so the session row + its users stayed in the cloud DB forever,validate-sessionkept reportingisActive: true, and the share URL stayed "live" after everyone left. Nowclosesends the proper signaling verb before disconnecting:- host →
delete_session(deletes the session + kicks all peers), - guest →
leave_session(removes just that user; the room stays up). Verified against production: after host close,validate-sessionreturnsexists:false / "Session not found"andsession-inforeturns 404; a guest leaving dropsuserCount2→1 while the session stays active.
- host →
- Guests now handle
session_deleted— when the host deletes the room, the guest daemon clears state and stops the reconnect loop instead of repeatedly trying to rejoin a session that no longer exists.
Verified end-to-end against prod (real two-peer test, no mocks)
Session create + share URL + /session-info + /validate-session discovery;
remote peer join + host approve; 20 MB WebTorrent transfer (byte-identical)
with live progress/speed/ETA stats; small WebSocket transfer; bidirectional
E2EE chat; guest leave; host delete.
[2.1.10] — 2026-06-30
Fixed — install/update 500s on cold cache (the real blocker)
- Cloud Run could not serve the ~96 MB binary on a cold cache miss. Confirmed
in prod: a full GET 500'd in ~0.3s and never even reached the app
(
X-Srift-Versionabsent), while a ranged GET returned 206 and DID reach the app. Cause: Cloud Run buffers/limits non-streamed responses; a single large body with a fixedContent-Lengthis rejected at the platform layer. Replacedexpress.staticfor/dl/*with an explicit handler that streams the binary with chunked Transfer-Encoding (streamed responses are exempt from the cap) and still honoursRangerequests. This is what fixes the cold-misscurl: (22) 500during install /self-update. - Pre-warm now does a full GET, not HEAD. A HEAD only warmed response headers, never the binary body — so the first real user still pulled it cold. A full GET streams the whole body through the edge once so subsequent users get a cache HIT.
Added
- No-install ephemeral runner (
/run.sh,/run.ps1). Run any srift command straight from the URL with nothing installed and PATH untouched — npx-style. The binary is cached under~/.cache/srift(or%LOCALAPPDATA%\srift\cache) and reused, so only the first call downloads.curl -fsSL https://srift.app/run.sh | sh -s -- quick-share ./file.zip& ([scriptblock]::Create((irm https://srift.app/run.ps1))) --versionVerified end-to-end against prod (download + checksum + run; 2nd run instant from cache).
Changed — faster downloads
- Binaries are now served gzip-compressed over the wire. The build produces
srift[.exe].gz(gzip -9) next to each binary;/dl/*serves the.gzwithContent-Encoding: gzip+Vary: Accept-Encodingwhen the client sendsAccept-Encoding: gzip. install.sh / install.ps1 / run.sh / run.ps1 / andself-updatenow passcurl --compressed, which decompresses transparently — so the bytes on disk (and the SHA-256 the installer verifies) are still the RAW binary. Roughly halves transfer size => noticeably faster installs/updates. Fully fallback-safe: if no.gzexists or the client doesn't ask, the raw binary is served exactly as before. The deploy integrity gate now verifies BOTH the raw and--compressedpaths hash to the published checksum. - Binaries built with
bun --compile --minifyto trim size. (Standalone bun binaries are still runtime-dominated, so a first download is network- bound; the edge cache + the no-install runner's local cache make repeat use instant.)
[2.1.9] — 2026-06-30
Fixed — critical install/update outage
- Checksum mismatch on every Windows/Unix install +
self-update(THE outage).bun --compileis non-deterministic, so each re-deploy of a version produced a different 100 MB binary. Cloudflare caches/dl/<v>/<target>/srift[.exe]asimmutablefor a year, but serves the tiny extension-lessSHA256SUMSDYNAMIC(uncached). v2.1.8 was deployed twice — so the edge kept serving the first build's binary against the second build's checksum, and every install/self-updateaborted withChecksum mismatch. Fixed by treating versioned URLs as truly immutable: bumped to 2.1.9 so installers hit fresh, un-poisoned/dl/2.1.9/*URLs where the binary andSHA256SUMSare cached together from one build. srift uninstallleft the binary behind on Windows. The runningsrift.exeis file-locked and cannot be deleted while executing, so the old path tried only a deferred delete. Now we rename the running exe out of the way first (Windows allows renaming a running binary), which frees the canonicalsrift.exepath and PATH resolution immediately, then schedule the renamed leftover for deletion. Uninstall now takes effect the instant it exits.
Added
- Post-deploy CDN integrity gate (
verify-cdn-integrityincloudbuild.yaml). After pre-warm, the deploy downloads every binary AND itsSHA256SUMSthrough the CDN and fails the build if any pair disagrees — so a stale-edge / re-deployed-version skew can never reach users again; it breaks the deploy instead.
Note
- The standalone binaries are ~96-100 MB (bun-compiled). This makes installs
slow and is the next optimization target (
--minify, compression).
2.1.8 — 2026-06-29
Industry-aligned hardening (researched + verified against real install scripts)
Cross-referenced our install + download flow against the official source of:
- rustup-init (Rust Foundation)
- deno_install (Deno) —
denoland/deno_install/install.ps1 - bun install.ps1 (Oven) —
bun.sh/install.ps1 - Cloudflare's
Error 500+ Bot Fight Mode docs - Google Cloud Run cold-start mitigation docs
Found three gaps in our 2.1.7 implementation and closed them:
Added
- Post-deploy Cloudflare edge cache pre-warming. New
pre-warm-cdnstep incloudbuild.yamlruns after the Cloud Run deploy. It pulls the live latest version from/cli/version.json, then issues 3curl --headrequests against every/dl/$VER/$target/srift[.exe]URL + everySHA256SUMSURL. This populates Cloudflare's edge cache at the POP closest to Google Cloud Build's runners, so the first 5-10 min after a release no longer return cold-cache 500s for the warming POP. (Other POPs still warm on their first user request, butstale-if-errorbelow covers that case for previously-cached URLs.) stale-while-revalidate+stale-if-errorCache-Control on/dl/*. Per RFC 5861 + Cloudflare's documented behaviour. If origin returns 5xx for any reason (cold-start, instance crash, deploy in progress), Cloudflare keeps serving the previously-cached version for up to 24 h. Eliminates transient install-time 500s permanently for any URL that has ever been served successfully before.- rustup-style security flags on every curl call.
--proto '=https'forces HTTPS-only,--tlsv1.2rejects TLS 1.0/1.1. Lifted directly from the Rust install team's recommended one-liner. - Custom User-Agent on every download (curl + IWR fallback + Node
https.get):
srift-installer/X.Y.Z (platform; arch)orsrift-cli/X.Y.Z (...). Default PowerShell IWR UA can trip Cloudflare bot heuristics in some configurations. A meaningful UA also makes CDN access logs useful for debugging.
Verified equivalence
Deno's official install.ps1 uses curl.exe --ssl-revoke-best-effort -Lo
(curl-primary, no IWR fallback). Bun's install.ps1 uses
curl.exe "-#SfLo" (curl-primary, IRM as documented fallback). Our 2.1.7
implementation already matched their pattern; 2.1.8 adds the remaining
hardening they ship (security flags + UA + retry depth).
2.1.7 — 2026-06-29
Fixed (the actual root cause of every install/update HTTP 500)
PowerShell's Invoke-WebRequest and Node's https.get were getting HTTP 500
from specific Cloudflare POPs (notably Mumbai/Singapore) for SRIFT binary URLs
— while curl from the same network got clean 200 OK responses on the
same URLs. Cloudflare differentiates response handling based on request
signature (header set, HTTP version, UA). IWR/Node were tripping a bad path.
Fix: every download path — install.ps1, install.sh, and
srift self-update (cli/index.ts) — now uses curl.exe as the primary
downloader. Windows 10 1803+ ships curl.exe in C:\Windows\System32. We
locate it explicitly (not via PATH, which can alias curl → IWR in PowerShell).
Falls back to IWR / Node https.get only when curl is unavailable.
Added — idempotent installers + safer flow
- Already-installed detection. Re-running
install.ps1/install.shon a machine where srift is present now compares versions viasrift --version:- same version →
"already installed — nothing to do"+ runssrift doctor, exits clean - older → upgrades (downloads + atomic swap)
- newer → refuses to downgrade unless
SRIFT_ALLOW_DOWNGRADE=1is set
- same version →
- Pre-flight internet check. A HEAD to
/cli/version.jsonupfront. Fails fast with "check your network/proxy/firewall" instead of retrying into a wall. - Live version discovery. Both installers now fetch
/cli/version.jsonand use whatever the live latest is, not what the script baked in. Handles cases where the install.sh/.ps1 file is older than the latest release. install.ps1install-time file-lock handling. IfCopy-Itemover an existing lockedsrift.exefails, stage tosrift.exe.newand schedule a self-cleaning.batto swap them ~2 s after exit. No more "install silently failed because the .exe was running".install.ps1sanity-check downloaded file size. If curl exits 0 but produces a <1 MB file (rare CDN bug), falls back to the next candidate version instead of installing a broken stub.SRIFT_ALLOW_DOWNGRADE=1env var to force a downgrade when needed.
2.1.6 — 2026-06-29
Fixed (the "brand-new release returns HTTP 500 for ~5-10 minutes" race)
When a new SRIFT version is deployed, the binaries land on the CDN origin (Cloud Run via Cloud Build) — but Cloudflare's edge cache at every POP is cold for the first few minutes. The first request from each POP misses the cache, hits Cloud Run, and sometimes 500s because Cloud Run is cold-starting while trying to stream a 100 MB binary. Until that POP's cache warms up, every install from that region fails — even with retry-on-5xx, because the 500 keeps coming back.
Fix: both install.sh and install.ps1 now build a candidate version
list = [requested, prev-patch, prev-prev-patch, ...]. They try the
latest first (with retry-all-errors). If it's still unreachable after 8
attempts, they fall back to 2.1.5, then 2.1.4, etc. — patch versions
that have been warm at every POP for hours/days. The installer then tells
the user to run srift self-update in 5-10 minutes to pick up the latest
once the CDN catches up.
This means: deploying a new SRIFT version never again strands users who try to install in the first 10 minutes after release.
Hot install commands that work right now during a CDN race
REM Force the previous-known-good version explicitly:
powershell -NoProfile -ExecutionPolicy Bypass -Command "$env:SRIFT_VERSION='2.1.3'; irm https://srift.app/install.ps1 | iex"
SRIFT_VERSION=2.1.3 curl -fsSL https://srift.app/install.sh | sh
2.1.5 — 2026-06-29
Fixed (audited every other "partial cleanup" spot in the codebase)
install.ps1&install.shnow do pre-install cleanup — they used to just drop the new binary in place, leaving any other srift on the system alone. That's how the user accumulated 6 ghost installs across%TEMP%dirs that kept shadowingsrift uninstall. Now both installers:- kill any running
sriftprocess, - scan every PATH directory (incl. Windows User PATH from the registry)
for stale
srift[.exe]binaries and remove them, - only then install the new one.
- kill any running
install.ps1download now retries 15× with 4s backoff on any failure — was a single-shotInvoke-WebRequestthat died on the first Cloudflare cache-miss 500 (same root cause as the broken 2.1.1srift self-update).install.shdownload now usescurl --retry-all-errors(curl 7.71+) which retries on HTTP 5xx. Falls back to a manual retry loop for older curls +wget --retry-on-http-errorfor wget systems.POST /session/closeon the daemon was leaking everything: WebTorrent client never destroyed,activeTorrentsmap never cleared, AES-256-GCM encryption key kept in memory after session close,.srift-temp/chunk files orphaned. Now does a proper full teardown.POST /resetwas wiping memory but leaving.srift-temp/orphans on disk forever. Now also cleans the temp dir + reportscleanedChunksin the response.POST /daemon/stopwas leaving the system inconsistent —.srift-state.jsonstill showed an active session after the daemon process died, and.srift-temp/chunks from interrupted transfers stuck around. Now does the same full teardown as/session/close+ writes a final stopped-state file so external readers see truth.
2.1.4 — 2026-06-29
Fixed (the "uninstall doesn't actually uninstall" bug)
srift uninstallnow wipes every srift trace from the system. Previously it only removed the one binary at~/.srift/bin/srift[.exe]and only stripped that one path from PATH. If you had other srift copies elsewhere (customSRIFT_INSTALL_DIRfrom a previous run, stale test installs in%TEMP%, duplicate copies on different PATH entries), eachsrift uninstallwould remove one and the next run ofsriftwould shadow through to another one. Now uninstall scanswhere srift/which -a srift, every PATH directory (including the Windows User PATH from the registry), and the canonical install path — de-dups, then removes all of them in one pass (with Windows file-lock handling for each). It then strips every srift- referencing entry from PATH (case-insensitive match), broadcastsWM_SETTINGCHANGE, and cleans up stalesrift-uninstall-*.bat/srift-update-*.batfiles in%TEMP%.srift self-updatenow removes stale duplicate binaries. After successfully installing the new version at~/.srift/bin/, it scans for any other srift binaries elsewhere on the system, removes them (deferred .bat for any that are locked), purges their PATH entries, and re-adds just the canonical install dir. You end up with exactly one srift on the system.srift uninstall --purgenow schedules a delayedrmdir /s /qvia self-cleaning.baton Windows when~/.srift/contains the running .exe (instead of silently failing with EBUSY).
Added
findAllSriftBinaries()— discovery helper used by uninstall + self-updatepurgeSriftFromPath()— strips srift PATH refs from registry / shell rcremoveBinary()— primitive that handles immediate delete + Windows scheduled-delete fallback- Uninstall reinstall hint now shows all 3 platforms (POSIX / PowerShell / cmd)
2.1.3 — 2026-06-29
Fixed (the "srift self-update just hangs forever" bug)
srift self-updateno longer stucks on a flaky download. Previously the downloader had no timeout, no retry, no resume, no progress. So once a transientHTTP 500came back from Cloudflare (cache-miss + Cloud-Run cold start), every subsequent run would silently hang atDownloading https://srift.app/dl/.../srift.exe ...until the user Ctrl-C'd. Rewritten downloader now:- prints
Downloaded XX.X MB (NN.N%)on the same line as bytes flow, - aborts the connection if no bytes arrive for 30 s,
- resumes via HTTP
Rangefrom the partial.exe.new, - retries up to 5× with exponential backoff (2s, 4s, 6s, 8s, 8s),
- prints the manual
install.ps1/install.shone-liner if all retries fail.
- prints
- Windows atomic-replace fallback in self-update. Renaming a new binary
over the running
srift.exethrowsEPERM/EBUSY. We now stage the swap via the same self-cleaning.batpattern used byuninstall— wait 2 s, delete the old .exe,movethe .new in place, .bat deletes itself. fetchRaw(used for SHA256SUMS) — added 15 s request timeout, single-hop redirect support, and proper non-2xx rejection. Was previously hanging on slow networks with no exit condition.
Added
- Aggressive Cloudflare caching on
/dl/*— the responses now carryCache-Control: public, max-age=86400, s-maxage=31536000, immutable. Every version-pinned binary URL is immutable by definition, so this lets the Cloudflare edge serve 99.99% of binary downloads without ever touching Cloud Run. Practically eliminates the cold-startHTTP 500users were seeing on first install /srift self-update.
2.1.2 — 2026-06-29
Fixed
srift uninstallon Windows now actually deletes the .exe. 2.1.1 printedScheduled binary removalbut the inlinestart "" /B cmd /C "timeout … & del /F /Q \"…\""form had a nested-quote bug that cmd.exe silently dropped, so the binary was never deleted. 2.1.2 writes a self-cleaning.batto%TEMP%(no inline quote escaping needed), retriesdel /f /qevery second until the .exe lock is released, anddel /f /q "%~f0"s itself when done.install.ps1Uninstall-Srift— same.bat-based scheduled-delete fix applied to the PowerShell uninstaller path.
2.1.1 — 2026-06-29
Fixed (critical — Windows + every Bun-compiled binary)
- Daemon now starts on the prod binary on every platform.
ensureDaemon()was spawningprocess.execPathwith Node-only flags (--experimental-strip-types) even when the running process was the Bun-compiled standalone CLI, which can't parse those flags. Result: every binary install on Windows / Linux / macOS failed withFailed to start daemon background processand no command that needs the daemon (quick-share,send,receive,list,session,mcp) worked. The CLI now detects when it is running as a Bun binary and self-spawnssrift daemon startas a detached child instead. install-mcpoutput is now usable. Previously it printed the Bun virtual-FS pathB:/~BUN/root/index.tsin the JSON snippet for Claude Desktop / Cursor / Continue / Codex — every config copied from this command was broken. It now emits the absoluteprocess.execPathof the actualsrift[.exe]binary with["mcp"]as args.srift uninstallactually removes the .exe on Windows. Windows holds an exclusive lock on a running .exe, so the in-processunlink()always threwEPERM. The CLI now schedules a detachedcmd /c timeout 2 & del /f /q …so the binary is deleted ~2 s after the command exits. Same fix applied toinstall.ps1'sUninstall-Sriftfunction.- WebTorrent native modules no longer crash the daemon at startup.
webtorrenttransitively requiresutp-nativeandnode-datachannel; Bun's--compilecannot bundle their.nodeartefacts, soimport WebTorrent from 'webtorrent'threwCannot find module '../build/Release/node_datachannel.node'on the Windows binary. WebTorrent is now lazy-loaded; if the natives are unavailable, the daemon stays up and transfers > 10 MB fall back to WebSocket-chunked. The/healthendpoint reportswebtorrent: false+webtorrent_errorso callers can detect this honestly.
Added
- Open CORS for browser-based AI agents. Every public discovery + API endpoint
on
https://srift.appnow servesAccess-Control-Allow-Origin: *so cross-origin tools (Claude.ai web, ChatGPT web, Perplexity, Gemini, browser extensions, Custom GPT actions) canfetch()it directly. Dashboard and any route bearing session cookies remain locked behind strict credentialed CORS. OPTIONSpreflight 204 for every public AI endpoint./install.bat— Command Prompt / cmd.exe wrapper that hands off to the PowerShell installer with full--uninstall/--purgepassthrough.- Windows PATH broadcast —
install.ps1now broadcastsWM_SETTINGCHANGEafter modifying user PATH (install + uninstall), so newly-spawned shells seesrifton PATH without requiring a reboot. install.shfriendly MINGW/MSYS/Cygwin handling — when run from Git Bash on Windows it now tells the user to useinstall.ps1instead of failing with a generic "Unsupported OS" message.- AGENTS.md §9b — explicit cross-origin / browser-based agent access policy.
public/llms.txt— added open-CORS paragraph in the authorization block.
2.2.0 — 2026-06-29 (rolled into 2.1.1 above)
Added
New CLI commands:
srift status [--json]— unified view: daemon health, active session, and transfer list in one callsrift daemon restart— gracefully stop the daemon (POST /reset then stop) and re-start itsrift daemon status [--json]— show only daemon health (version, uptime, mcp, webtorrent)srift reset [--json]— wipe session state and flush encryption keys viaPOST /resetsrift logs [--tail <n>] [--json-stream]— view daemon logs viaGET /logs(default 50 lines)srift uninstall [--purge]— remove the srift binary and clean PATH entries;--purgealso deletes~/.srift/
CLI robustness:
ensureDaemon(): increased wait from 3s to 5s (50 × 100ms iterations)ensureDaemon(): added port-already-in-use detection with actionable error messageensureDaemon(): timeout error now includessrift daemon start,srift logs, andsrift doctorhints- Unknown command now prints
Did you mean: srift <closest-command>suggestion - New
--no-daemonglobal flag to skip auto-start for status-only invocations daemonRequest()shared helper for typed HTTP requests to the local daemon
Install scripts:
install.sh: added--uninstall [--purge]option —curl -fsSL https://srift.app/install.sh | sh -s -- --uninstallinstall.sh: post-install step now runssrift doctorto verify the installation end-to-endinstall.sh: binary verify now prints OS/arch/file-size debug info if the binary fails to runinstall.ps1: added--uninstall [--purge]optioninstall.ps1: same post-install doctor check and debug output- Both scripts: next-steps banner updated with
srift statusand uninstall command
Documentation:
AGENTS.md §6: full CLI command reference updated with all new commandsai-instructions.md §6: same.cursorrules: samepublic/llms.txt: new "CLI commands" section added before observability endpointspublic/compat.json:releaseDatebumped to 2026-06-29public/changelog.json: new 2.2.0 entryapp/ai-agents/page.tsx: "Maintenance & Updates" CLI group updated with all new commands
2.1.0 — 2026-06-28
Added
Daemon API — all documented endpoints now fully implemented:
GET /health— liveness probe ({ok, version, uptime_ms, mcp, webtorrent})GET /transfers— list active transfers with state, progress, and speedGET /transfers/:fileId— single-transfer detailGET /peers— connected peer list with RTT and connection methodGET /metrics— Prometheus-format counters (transfers, bytes, errors)GET /logs?n=50— NDJSON daemon log tailPOST /reset— flush state + encryption keys (full daemon-side wipe)GET /api/v1/monitor/events— SSE stream (connection_state,join_request,file_offer,transfer_progress,chat_received)
Response headers on every daemon response:
X-Srift-Daemon: <version>— daemon versionX-Srift-Wire: 2— wire protocol versionX-Srift-Min-Sdk: 2.0.0— minimum compatible SDK
Versioning infrastructure:
GET /sdk/<lang>/version.json— per-SDK freshness endpoint (11 languages)GET /cli/version.json— CLI binary freshness check with per-platform download URLsGET /compat.json— daemon ↔ SDK compatibility matrixGET /changelog.json— machine-readable changelog
SDK distribution:
GET /install.sh— universal POSIX installer (macOS, Linux, WSL, Termux, ARM)- Auto-detects OS + CPU architecture
- SHA-256 checksum verification
- Automatic PATH configuration (bash/zsh/fish)
SRIFT_VERSION,SRIFT_INSTALL_DIR,SRIFT_NO_VERIFYenv overrides
GET /install.ps1— Windows PowerShell installer (PowerShell 5.1+, pwsh 7+)- Same feature parity as install.sh
New CLI commands:
srift version [--json]— print CLI version, daemon version, Node version, update availabilitysrift self-update [--json]— atomic in-place binary update with SHA-256 verificationsrift doctor [--json]— health check: daemon, network, version, configsrift config [get|set|delete] [key] [value]— manage~/.srift/config.json
Background update checks:
- After every CLI command, a silent background HTTP check fires against
/cli/version.json - If an update is available, a one-line nudge is printed to stderr
- Throttled to once per 24h (configurable via
srift config set updateCheckIntervalHours 48) - Opt-out:
srift config set updateCheck falseorSRIFT_NO_UPDATE_CHECK=1
Agent discovery:
GET /.cursorrules— now served at production (was 404 before)GET /CHANGELOG.md— served at production (linked from AGENTS.md)
SDK downloads now include:
Content-Disposition: attachment; filename="<file>"— forces correct filename on saveX-Srift-Version: <version>— SDK version tagX-Srift-Min-Daemon: 2.0.0— minimum compatible daemonLink: <…>; rel="latest-version"— machine-readable canonical URL
Fixed
/ai-agentspage — daemon ping:pingDaemon()was fetching/health(404 on daemon). Fixed to/healthz./ai-agentspage — STATS_ENDPOINTS:/eventscorrected to/api/v1/monitor/events./ai-agentspage — TROUBLESHOOTING:srift daemon --start/--stop→srift daemon start/stop(flags were wrong; CLI only accepts subcommands)./ai-agentspage — HTTP_ERRORS: Same daemon flag corrections.- SDK install commands: Java, C#, PHP, Ruby now show explicit
curl -O https://srift.app/sdk/<lang>/<file>commands (were showing require/import without download step). - Node SDK install: Changed from
npm install srift(package not yet published) tocurl -O https://srift.app/sdk/node/srift.mjs. - Go SDK install: Changed from
go get sripto.tech/sdk/go/srift(module not yet published) tocurl -O https://srift.app/sdk/go/srift.go. - Rust SDK install: Changed from Cargo.toml snippet to
curl -O https://srift.app/sdk/rust/srift.rs. - PowerShell SDK install: Added
Invoke-WebRequestdownload step (was showingImport-Module ./srift.ps1with no download). - PowerShell SDK path:
sdk/powershell/srift.ps1now served (file previously only existed atsdk/shell/srift.ps1). POST /session/approve: Returns404 {error: "Unknown tempUserId"}for invalid IDs (was silently succeeding).POST /chat/send: Returns503 {error, retryAfterMs}when WebSocket connecting (was returning 500).
Security
- SHA-256 checksum verification for binary downloads in
install.shandinstall.ps1 - No auto-update of SDK library files (supply chain risk). Only the CLI binary self-updates.
2.0.0 — 2026-06-26
Added
- Initial public release
- MCP server (stdio + HTTP streamable transport, spec 2025-06-18 + fallback 2024-11-05)
- REST daemon API on
http://127.0.0.1:3822 - 11 SDKs: Python, Node.js/TS, Go, Rust, Java, C#, PHP, Ruby, Bash, PowerShell, curl
- 26 integrations: OpenAI, Anthropic, Gemini, Grok, Cohere, LangChain, AutoGen, CrewAI, and more
- WebRTC (WebTorrent) + WebSocket fallback for P2P file transfer
- AES-256-GCM + PBKDF2-SHA256 (100k iter) end-to-end encryption
- OpenAPI 3.1.0 spec with 25 endpoints at
/openapi.json - Discovery endpoints:
/.well-known/mcp/server-card.json,/.well-known/ai-plugin.json,/.well-known/agent.json - CLI:
srift quick-share,session,send,receive,monitor,approve,reject,kick,chat,mcp,install-mcp,info - AGENTS.md, .cursorrules, ai-instructions.md, public/llms.txt for AI agent discovery
Versioning Policy
| Bump | When |
|---|---|
PATCH x.y.Z | Bug fixes, documentation, no behaviour change |
MINOR x.Y.0 | New endpoints, new SDK features — backwards compatible |
MAJOR X.0.0 | Breaking API changes, wire protocol bump, auth scheme change |
API stability promise:
- Routes under
/api/v1/are stable until a major bump. - Breaking changes get a 90-day deprecation window with
Deprecation+Sunsetresponse headers. - All SDKs (Python/Node/Go/etc.) follow the same SemVer as the daemon.