slicc-cdp-client
Shared Chrome DevTools Protocol connection for the playwright-cli and curlwright binaries.
The crates.io name is slicc-cdp-client. The Rust library name stays cdp_client:
cdp-client = { package = "slicc-cdp-client", version = "1" }
One browser WebSocket. Target.attachToTarget uses {targetId, flatten: true}. Later page commands send that sessionId at the top level. There is no /devtools/page/<id> socket.
Fetch interception
cdp_client::fetch pauses requests on one target session. Interception::attach(&mut cdp, target_id, &patterns) checks the target, attaches, and sends Fetch.enable; dropping it, or finish(), sends Fetch.disable and detaches. Interception::on_session does the same on a session you already hold and leaves it attached. cdp::detach also forgets that session's paused requests.
Pattern::request(glob)andPattern::response(glob)are CDPurlPatterns (*,?,\to escape) at the Request or Response stage, with an optionalresource_type.next(timeout)returns aPaused:method,url,url_fragment,full_url(),headers,post_data, and at the response stagestatus,response_headersandlocation(), which resolvesLocationagainst the request URL and keeps its fragment.decide(&paused, &Decision)sendsContinue(Overrides)(continueRequestwith URL, method, body or headers),ContinueResponse(ResponseOverrides),Fulfill(Fulfill)(fulfillRequest: status, headers, body;Fulfill::signed_in()is a short "Signed in, you can close this tab" page) orFail(ErrorReason)(failRequest).response_body(&paused)isFetch.getResponseBody, decoded.navigation(guard, event)reads aPage.frameNavigatedevent asurlplusurlFragment, for a redirect made by script.Cdp::set_decider(session, Some(Box<dyn FnMut(&Paused) -> Decision>))answers pauses inline, even while another call waits for its reply, so a synchronousPage.navigatedoesn't deadlock. A decision CDP rejects is retried as a plain continue and kept intake_fetch_errors().
Seven's origin is off limits
Cdp filters every command and event the way slicc-cli's cdp relay does (slicc-cli README, "Seven's origin is off limits"). What is protected is protectedOrigins from the slicc-browsers crate: every sliccy.ai host, vault.sliccy.ai by name, blob:, filesystem: and view-source: URLs by their inner URL, devtools:, and the extra origins from Cdp::protect_origins(&[...]). protected::Protected parses URLs with a WHATWG parser and passes every vector in protected.json. Refusals are slicc: refused: target is on seven's origin, or a reason of their own after slicc: refused: . When the connection itself closes, the close frame (websocket closed <code> <reason>) is reported, never a refusal.
- Targets. A target is protected when its URL is, or when it is on
about:blank(or another inheriting URL) and was protected before or has a protected or unknown opener. A page session also becomes protected when its main frame commits a protected URL orsecurityOrigin(Page.frameNavigated), when its default context's origin is protected (Runtime.executionContextCreated), or when the target moves there (Target.targetInfoChanged). Such a session is detached, its events and results are dropped, and every later command on it is refused. - Refused:
Target.createTarget,Page.navigateand anything else whoseurlis protected;Page.navigateToHistoryEntryto a protected entry;attachToTarget,activateTarget,closeTarget,Browser.getWindowForTargetand any command whosetargetIdorframeIdis a protected target; every command on a protected session.Target.getTargetInfoandTarget.detachFromTargetstay allowed. - Origins, storage and cookies. A command whose
origin,securityOrigin,topLevelSite,scopeURL(and the rest of slicc-cli's list),storageKeyorcacheIdnames a protected origin is refused. Free-form values (expression,arguments,value,headers,postData) aren't searched. Cookie readers answer without protected cookies, and cookie writers for a protected domain, URL or partition site are refused withslicc: refused: cookies on seven's origin. So areNetwork.clearBrowserCookiesandStorage.clearCookies, except in a browser context thisCdpcreated. - Browser-wide:
Target.exposeDevToolsProtocol,Target.sendMessageToTarget,Target.attachToBrowserTarget,Tracing.start,Extensions.loadUnpacked,Fetch.*without a session, permissions without an origin, andBrowser.resetPermissionsoutside an own context.Target.setAutoAttachwithautoAttach: trueis refused: slicc-cdp does not support auto-attach. - Requests a protected document makes. When a touched target commits a protected origin all the same (back/forward cache, a protected
blob:), seen by its guard session'sPage.frameNavigatedorRuntime.executionContextCreated, every page session on it getsFetch.disableand is detached at once, and its pending pauses are continued unchanged. A paused request whose frame is a protected document, whose target is protected, or whoseRefererorOriginis protected is continued unchanged and never reachesnextor a decider, even when its URL isn't protected. ItsNetworkevents (bydocumentURL,initiatororframeId) are dropped. - Requests. A protected request's
Network.*andFetch.*events are dropped,ExtraInfoevents are held until their request is known, and seven's cookies leaveassociatedCookies. Its body getters (Network.getResponseBody,getRequestPostData,Fetch.getResponseBody,replayXHRand the rest) are refused, and so is one for a request thisCdpnever saw (slicc: refused: slicc hasn't seen this request, so it could be seven's).IO.readworks only on a stream thisCdphanded out. - Navigation by other means. A target this
Cdpattaches to or creates is touched. On it,Cdpattaches a session of its own, enablesFetchfor document requests at the request stage plusNetwork.setBypassServiceWorker, and fails each document request into a protected origin withBlockedByClient. A script'slocation, a link, a meta refresh or a server redirect into the vault then shows Chrome's error page, and the target is protected from then on.Target.createTargetopensabout:blank, touches it, and then navigates. The guard setup (Target.setDiscoverTargetsthe first time, the attach,Fetch.enable,Network.enableandNetwork.setBypassServiceWorker) shares one 30 s budget and fails closed when it runs out, refused as…out of this target: <step> timed out.Page.enableandRuntime.enableon that session are optional. When one of them times out, the setup goes on,Cdp::unguarded_optional(target)lists it, andCdp::take_notices()returnsslicc: guard: <step> timed out; continuing, whichplaywright-cliandcurlwrightprint on stderr. A paused document request to a protected origin on your ownFetchis failed the same way, and other protected requests are continued unchanged; neither reachesnextor a decider. - Listing.
Target.getTargets,getTargetInfo,targetCreatedandtargetInfoChangedstill show protected targets, but theirurlandtitleare only the origin.Cdp::is_protected_target(id)answers from what thisCdphas seen (listings, commits, refused sessions).Cdp::target_protected(id, false)asks the browser now, a tab that landed on the vault included, and records the answer, so both agree afterwards.playwright-cliandcurlwrightleave them out entirely. - Limits. As in slicc-cli: a document that arrives without a request (back/forward cache, a protected
blob:) is caught only after it commits.
fetch::enable refuses a pattern whose host part names a protected host (parsed, so notsliccy.ai and ?next=https://seven.sliccy.ai/ pass). A decision can't redirect to a protected URL by url, Location or Refresh.
use ;
let mut seen = attach?;
let session = seen.session_id.to_string;
seen.notify;
if let Some = seen.next?
Host builds use native-tls for wss:// and for https:// discovery, on macOS, Linux, and Windows. Rust 1.83 or newer. The wasm32-wasip1 build has no TLS. This crate does not launch Chrome. Callers pass a DevTools WebSocket URL or an http:// discovery URL.
cargo install slicc-cdp installs the playwright-cli command. cargo install curlwright installs curlwright. @ai-ecoverse/slicc-cdp on npm ships their WASI builds for SLICC.