Technical Documentation: mountain-loop/yaak
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
๐ซ Yaak โ Desktop API Client ๐ซ
Install
Download Yaak for macOS, Windows, or Linux from yaak.app/download. Package managers and other install methods are in the installation docs.
Features
Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it.
Built with Tauri, Rust, and React, itโs fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in.
๐ Work with any API
- Import collections from Postman, Insomnia, OpenAPI, Swagger, or Curl.
- Send requests via REST, GraphQL, gRPC, WebSocket, or Server-Sent Events.
- Filter and inspect responses with JSONPath or XPath.
๐ Stay secure
- Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication.
- Secure sensitive values with encrypted secrets.
- Store secrets in your OS keychain.
โ๏ธ Organize & collaborate
- Group requests into workspaces and nested folders.
- Use environment variables to switch between dev, staging, and prod.
- Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox.
๐งฉ Extend & customize
- Insert dynamic values like UUIDs or timestamps with template tags.
- Pick from built-in themes or build your own.
- Create plugins to extend authentication, template tags, or the UI.
Contribution Policy
[!IMPORTANT]
Community PRs are currently limited to bug fixes.
If your PR is not a bug fix, link the feedback item where @gschier explicitly gave you permission to work on it.
SeeCONTRIBUTING.mdfor policy details andDEVELOPMENT.mdfor local setup.
Useful Resources
License
The source is MIT licensed and can be built and run for free, for both personal and commercial use.
The prebuilt binaries follow Yaak's pricing terms: free for personal use, license required for commercial use.
Overview (`crates-cli/yaak-cli/README.md`)
Yaak CLI
The yaak CLI for publishing plugins and creating/updating/sending requests.
Installation
npm install @yaakapp/cliAgentic Workflows
The yaak CLI is primarily meant to be used by AI agents, and has the following features:
schemasubcommands to get the JSON Schema for any model (eg.yaak request schema http)--json '{...}'input format to create and update data--verbosemode for extracting debug info while sending requests- The ability to send entire workspaces and folders (Supports
--paralleland--fail-fast)
Agent Setup
Install the Yaak skill so your coding agent knows how to drive the CLI:
yaak agent install # every agent found on this machine
npx skills add mountain-loop/yaak --skill use-yaak # or with the skills CLIClaude Code and Codex can install it as a plugin instead. In Claude Code, run/plugin install yaak --marketplace mountain-loop/yaak; the plugin also registers theyaak mcp server. In Codex, run codex plugin marketplace add mountain-loop/yaak,
then codex plugin add yaak@yaak.
Any MCP client can run the server over stdio:
{
"mcpServers": {
"yaak": { "command": "npx", "args": ["-y", "@yaakapp/cli", "mcp"] }
}
}Example Prompts
Use the yaak CLI with agents like Claude or Codex to do useful things for you.
Here are some example prompts:
Scan my API routes and create a workspace (using yaak cli) with
all the requests needed for me to do manual testing?Send all the GraphQL requests in my workspaceDescription
Here's the current print of yaak --help
Yaak CLI - API client from the command line
Usage: yaak [OPTIONS] <COMMAND>
Commands:
auth Authentication commands
plugin Plugin development and publishing commands
send Send a request, folder, or workspace by ID
workspace Workspace commands
request Request commands
folder Folder commands
environment Environment commands
Options:
--data-dir <DATA_DIR> Use a custom data directory
-e, --environment <ENVIRONMENT> Environment ID to use for variable substitution
-v, --verbose Enable verbose send output (events and streamed response body)
--log [<LEVEL>] Enable CLI logging; optionally set level (error|warn|info|debug|trace) [possible values: error, warn, info, debug, trace]
-h, --help Print help
-V, --version Print version
Agent Hints:
- Template variable syntax is ${[ my_var ]}, not {{ ... }}
- Template function syntax is ${[ namespace.my_func(a='aaa',b='bbb') ]}
- View JSONSchema for models before creating or updating (eg. `yaak request schema http`)
- Deletion requires confirmation (--yes for non-interactive environments)Overview (`crates-server/yaak-playground/README.md`)
yaak-playground
The API behind yaak.run, for trying Yaak without bringing your own.
- Users and todos are fixed sample data.
- Posts can be created, changed, and deleted. Each client IP gets its own copy, which goes back
to the sample dataYAAK_PLAYGROUND_RESET_AFTER_SECSafter its first write. POST /auth/tokenreturns a bearer token forGET /auth/me.POST /auth/sessionsets a
session cookie forGET /auth/session. Any username with the passwordyaaklogs in.GET /lists every endpoint andGET /openapi.jsondescribes them.
Nothing is written to disk, so a restart resets everything.
cargo run -p yaak-playground # http://127.0.0.1:9228Run yaak-playground --help for the limits. Behind a load balancer that setsX-Forwarded-For, set YAAK_PLAYGROUND_TRUST_FORWARDED_FOR=true.
The image is Dockerfile.playground at the repo root.
Overview (`crates-server/yaak-web/README.md`)
yaak-web
The network half of Yaak in a browser โ and, with --serve, the half that
hands the browser the app in the first place.
A tab can't see an HTTP response the way a desktop app can: CORS hides most
headers (2 of 8 in a typical response), redirects are followed silently, and
there is no timeline. So the tab renders the request and posts it here, and this
process puts it on the network with the desktop's own engine (yaak-http) and
streams back everything that happened โ every header, every redirect hop, DNS
timing, the body โ for the tab to store.
It is a stateless executor. It keeps nothing: no database, no files, no
sessions, no cookies between calls. Every byte it sees comes from the tab in the
request, and every byte it returns is stored by the tab. Restart it any time.
Self-hosting it
One container, no configuration, nothing behind it:
docker run -p 8080:8080 ghcr.io/mountain-loop/yaak-webOpen http://localhost:8080. The image carries the built web client and this
binary, which serves it โ so the app and its sends are on one origin, and the
tab's send URL is a path (/v1/http/send) rather than an address anyone has to
configure. The image is linux/amd64 and linux/arm64, built fromDockerfile.web at the repo root.
Your data lives in your browser (SQLite compiled to wasm, in IndexedDB), not in
the container. The container is stateless: nothing is written to disk, so
upgrading is docker pull and nothing else.
Two settings are worth knowing about:
docker run -p 8080:8080 \
-e YAAK_WEB_ALLOW_PRIVATE_NETWORKS=true \
-e YAAK_WEB_RATE_LIMIT_PER_MINUTE=0 \
ghcr.io/mountain-loop/yaak-webYAAK_WEB_ALLOW_PRIVATE_NETWORKS=truelets sends reach loopback,
private and link-local addresses. Off by default, and it should stay off on
anything strangers can reach โ see What it refuses.
Turn it on for an instance on your own network, where calling the API on the
next machine is the whole point. Note that "private" is relative to the
container:127.0.0.1is the container itself, and reaching the Docker
host meanshost.docker.internal(or--network host).YAAK_WEB_RATE_LIMIT_PER_MINUTEdefaults to 120 sends per client IP,
which suits a public instance and not a team of your own;0disables it.
Behind a reverse proxy, add YAAK_WEB_TRUST_FORWARDED_FOR=true so the rate
limit sees real client addresses instead of its own โ and only then, since
otherwise anyone can spoof the header. If the reverse proxy buffers responses,
tell it not to: sends are streamed, and the X-Accel-Buffering: no header this
binary sets is honoured by nginx-shaped ones.
Running it from source
cargo run -p yaak-web -- --serve dist/apps/yaak-clientafter a YAAK_TARGET=web SKIP_WASM_BUILD=1 npx vp -C apps/yaak-client build.
Without --serve it is the send executor alone, which is what the frontend
dev server wants:
cargo run -p yaak-web
YAAK_TARGET=web npm run dev --workspace @yaakapp/yaak-clientThe dev server passes /v1 through to this binary, so a dev build sends to its
own origin exactly like a production build does โ one address to open, and no
CORS in the loop. PORT moves this server and the dev server follows it. A
production build also sends to its own origin, unless VITE_YAAK_WEB_URL was
set when it was built.
Reaching the dev server from another machine is HOST=0.0.0.0. Vite allows
addresses but not names, so opening it as a hostname also needsALLOWED_HOSTS=that-name.
Configuration
Every flag has an environment variable, so a container needs no arguments;--help lists them all. The listen address uses the platform conventions โHOST and PORT โ so the published image runs unchanged on a
platform-as-a-service that assigns a port. Everything else is YAAK_WEB_*.
| Flag | Default | What |
|---|---|---|
--serve |
off | Also serve a built web client from this directory, on the same origin. |
--host |
127.0.0.1 |
Interface to listen on. The image sets 0.0.0.0. May be a name; localhost resolves. |
--port |
9227 |
Port to listen on. The image sets 8080. |
--allow-private-networks |
off | Allow sends to loopback, private and link-local addresses. |
--allowed-origins |
* |
CORS origins, comma-separated. Unused when the app is served from here: same origin, no CORS. |
--max-request-bytes |
16 MiB | Largest rendered request accepted from the tab. |
--max-response-bytes |
64 MiB | Largest upstream body relayed before the send is cut off. |
--max-timeout-secs |
60 | Ceiling on a send's timeout; a request asking for more (or none) gets this. |
--rate-limit-per-minute |
120 | Sends per client IP per minute; 0 disables. |
--max-concurrent |
256 | Sends in flight at once. |
--trust-forwarded-for |
off | Take the client IP from X-Forwarded-For. Only behind a load balancer that sets it. |
Logging
Default logs contain a startup message and operational warnings and errors, not request URLs, client IPs,
DNS lookups, or per-request timing. To diagnose a self-hosted instance, opt in withRUST_LOG=warn,yaak_http=error,yaak_web=debug. This includes request URLs and IPs,
so only enable it while debugging. Hosting infrastructure may maintain its own
access logs independently.
Serving the app
--serve DIR puts a file server behind the API routes: /v1/* is matched
first, everything else comes from DIR, and a path with no file behind it getsindex.html so the app's own routes survive a refresh. Responses are compressed
(gzip or zstd) on the fly. /assets/* is cached forever โ Vite content-hashes
those names โ and everything else is no-cache, so a new deploy arrives on the
next reload.
Serving files changes nothing about sending: the same rendered request, the same
destination policy, the same stateless executor. It exists so that a
self-hosted Yaak is one thing to run rather than two.
Split deployments
The app and the sender can still be separate services โ one CDN-hosted bundle and
one server elsewhere, or one server shared by several fronts. Then the bundle has
to be told where to send, at build time:
docker build -f Dockerfile.web \
--build-arg VITE_YAAK_WEB_URL=https://send.example.com .and the server needs the CORS origins its callers use, since the requests are no
longer same-origin:
docker run -p 8080:8080 \
-e YAAK_WEB_ALLOWED_ORIGINS=https://yaak.example.com \
ghcr.io/mountain-loop/yaak-web \
yaak-webThe trailing yaak-web is a command override: the same image run without--serve, so it executes sends and serves no app.
What it refuses, and why
A hosted sender is, by construction, a machine that makes HTTP requests on
behalf of strangers. Left alone that is an open relay into whatever network it
sits on. So by default it refuses to connect to:
- loopback (
127/8,::1), private (10/8,172.16/12,192.168/16,fc00::/7), link-local (169.254/16โ where cloud metadata lives โ andfe80::/10), carrier-grade NAT, multicast, reserved and unspecified ranges,
IPv4 addresses carried inside IPv6 forms (::ffff:a.b.c.d, the well-known
NAT64 prefix, 6to4), and the whole NAT64 local-use range; - anything not
http://orhttps://.
The check runs on the resolved addresses, after DNS, for every hop of a
redirect chain, so a public hostname that points at an internal address is
caught, and so is a Location: header that points at one. It also refuses body
types that would read files on its own disk (binary, multipart file
fields), since no browser tab could legitimately mean those.
Refusal reasons are included in debug logs when enabled. On a public instance
(web.yaak.app, or anything else strangers can reach), private-network protection
must stay on: the machine's private
network is the host's, not the user's, so a localhost or LAN API is not the
user's to reach through it โ the desktop app is what reaches those. On an
instance you run for yourself, that reasoning is inverted, and--allow-private-networks inverts the policy with it. It allows every range
above, including 169.254.169.254, so use it only where the network on the
other side is one the users are entitled to.
There is no authentication either way: an instance is anonymous, protected by
the per-client rate limit and the destination policy. Anything more (a shared
token, per-user quotas) is a later slice and would sit in front of send_http
in lib.rs, or in middleware on the exported router. Put TLS in front of a public instance.
The wire
POST /v1/http/send with a JSON body:
{
"request": { "url": "https://โฆ", "method": "GET", "headers": [โฆ], "body": {โฆ}, "bodyType": null, "urlParameters": [โฆ] },
"settings": { "validateCertificates": true, "followRedirects": true, "timeoutMs": 0, "sendCookies": true, "storeCookies": true },
"cookies": [ โฆ ]
}request is a Yaak HttpRequest in the desktop's own model shape with every
template already rendered by the tab; the server builds the URL, headers and
body from it exactly the way the desktop does after rendering. cookies is the
jar's contents (or null for no jar).
The reply is application/x-ndjson, one JSON frame per line, in the order things
happened:
type |
When | Carries |
|---|---|---|
event |
as the engine produces them | one timeline event, in the desktop's http_response_event.event shape |
response |
once, when the final hop's headers arrive | status, all headers, request headers as sent, remote address, HTTP version, timing |
body |
as the body is read | a decompressed chunk, base64 |
done |
last, on success | elapsed, byte counts, and the cookie jar as the send left it |
error |
last, on failure | the reason, and any cookies collected before the failure |
Refusals that happen before anything is sent (a blocked destination, a bad body,
rate limit, capacity) are plain HTTP errors (403, 400, 429, 503) with{"error": "โฆ"}, not streams.
Why a streamed HTTP response and not a WebSocket: one POST is stateless by
construction, cancellable by closing the connection, readable with curl, and
needs no upgrade handling on either side. A WebSocket only earns its keep when
traffic is bidirectional, which a single send is not.
The TypeScript side of this contract is generated from src/wire.rs by ts-rs
into bindings/ (run cargo test -p yaak-web after changing a frame)
and published to the tab as @yaakapp-internal/web, so a change to the
wire on one side is a type error on the other.
GET /v1/health reports the version and the effective limits.
What comes later
Not built, by design, but the router is shaped for it: a WebSocket relay
(/v1/ws/relay) and a gRPC relay (/v1/grpc/relay) would be long-lived,
bidirectional endpoints on the same binary, behind the same destination policy
and limits. They differ from this endpoint in holding per-connection
in-memory state while a connection is open (never persisted), which brings
connection limits and a larger abuse surface โ the reason they are separate
work.
Overview (`packages/plugin-runtime-types/README.md`)
Yaak Plugin API
Yaak is a desktop API client for
interacting with REST, GraphQL, Server Sent Events (SSE), WebSocket, and gRPC APIs. It's
built using Tauri, Rust, and ReactJS.
Plugins can be created in TypeScript, which are executed alongside Yaak in a NodeJS
runtime. This package contains the TypeScript type definitions required to make building
Yaak plugins a breeze.
Quick Start
The easiest way to get started is by generating a plugin with the Yaak CLI:
npx @yaakapp/cli generateFor more details on creating plugins, check out
the Quick Start Guide
Installation
If you prefer starting from scratch, manually install the types package:
npm install -D @yaakapp/apiOverview (`plugins-external/faker/README.md`)
Yaak Faker Plugin
This is a template function that generates realistic fake data
for testing and development using FakerJS.
Example JSON Body
Here's an example JSON body that uses fake data:
{
"id": "${[ faker.string.uuid() ]}",
"name": "${[ faker.person.fullName() ]}",
"email": "${[ faker.internet.email() ]}",
"phone": "${[ faker.phone.number() ]}",
"address": {
"street": "${[ faker.location.streetAddress() ]}",
"city": "${[ faker.location.city() ]}",
"country": "${[ faker.location.country() ]}",
"zipCode": "${[ faker.location.zipCode() ]}"
},
"company": "${[ faker.company.name() ]}",
"website": "${[ faker.internet.url() ]}"
}This will generate a random JSON body on every request:
{
"id": "589f0aec-7310-4bf2-81c4-0b1bb7f1c3c1",
"name": "Lucy Gottlieb-Weissnat",
"email": "[email protected]",
"phone": "411.805.2871 x699",
"address": {
"street": "846 Christ Mills",
"city": "Spencerfurt",
"country": "United Kingdom",
"zipCode": "20354"
},
"company": "Emard, Kohler and Rutherford",
"website": "https://watery-detective.org"
}Available Categories
The plugin provides access to all FakerJS modules and their methods:
| Category | Description | Example Methods |
|---|---|---|
airline |
Airline-related data | aircraftType, airline, airplane |
animal |
Animal names and types | bear, bird, cat, dog, fish |
color |
Colors in various formats | human, rgb, hex, hsl |
commerce |
E-commerce data | department, product, price |
company |
Company information | name, catchPhrase, bs |
database |
Database-related data | column, type, collation |
date |
Date and time values | recent, future, past, between |
finance |
Financial data | account, amount, currency |
git |
Git-related data | branch, commitEntry, commitSha |
hacker |
Tech/hacker terminology | abbreviation, noun, phrase |
image |
Image URLs and data | avatar, url, dataUri |
internet |
Internet-related data | email, url, ip, userAgent |
location |
Geographic data | city, country, latitude, longitude |
lorem |
Lorem ipsum text | word, sentence, paragraph |
person |
Personal information | firstName, lastName, fullName |
music |
Music-related data | genre, songName, artist |
number |
Numeric data | int, float, binary, hex |
phone |
Phone numbers | number, imei |
science |
Scientific data | chemicalElement, unit |
string |
String utilities | uuid, alpha, alphanumeric |
system |
System-related data | fileName, mimeType, fileExt |
vehicle |
Vehicle information | vehicle, manufacturer, model |
word |
Word generation | adjective, adverb, conjunction |
Overview (`plugins-external/httpsnippet/README.md`)
Yaak HTTP Snippet Plugin
Generate code snippets for HTTP requests in various languages and frameworks,
powered by @readme/httpsnippet.
How It Works
Right-click any HTTP request (or use the ... menu) and select Generate Code Snippet.
A dialog lets you pick a language and library, with a live preview of the generated code.
Click Copy to Clipboard to copy the snippet. Your language and library selections are
remembered for next time.
Supported Languages
Each language supports one or more libraries, listed in the order they appear in the
dialog:
| Language | Libraries |
|---|---|
| Agent | Agent Prompt |
| C | Libcurl |
| Clojure | clj-http |
| Crystal | http::client |
| C# | HttpClient, RestSharp |
| Go | NewRequest |
| HTTP | HTTP/1.1 |
| Java | AsyncHttp, java.net.http, OkHttp, Unirest |
| JavaScript | XMLHttpRequest, Axios, fetch, jQuery |
| Kotlin | OkHttp |
| Node.js | HTTP, Axios, fetch |
| Objective-C | NSURLSession |
| OCaml | CoHTTP |
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
| Powershell | Invoke-WebRequest, Invoke-RestMethod |
| Python | Requests |
| R | httr |
| Ruby | net::http, faraday |
| Rust | reqwest |
| Shell | cURL, HTTPie, Wget |
| Swift | URLSession |
The Agent target produces a prompt describing the request rather than code, for handing
to an AI coding agent.
Features
- Renders template variables before generating snippets, so the output reflects real values
- Supports all body types: JSON, form-urlencoded, multipart, GraphQL, and raw text
- Includes authentication headers (Basic, Bearer, and API Key)
- Includes query parameters and custom headers
Overview (`plugins-external/mcp-server/README.md`)
Yaak MCP Server Plugin
Exposes Yaak's functionality via the Model Context Protocol (MCP).
Deprecated. Yaak now has a built-in MCP server with the same tools. Uninstall this plugin,
then turn the server on in Settings > Agents, or run yaak mcp from the Yaak CLI.
Setup
Add this to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"yaak": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://127.0.0.1:64343/mcp"]
}
}
}Restart Claude Desktop and make sure Yaak is running.
Available Tools
list_http_requests- List all HTTP requests in a workspaceget_http_request- Get details of a specific HTTP requestsend_http_request- Send an HTTP request and get the responsecreate_http_request- Create a new HTTP requestupdate_http_request- Update an existing HTTP requestdelete_http_request- Delete an HTTP requestlist_folders- List all folders in a workspacelist_workspaces- List all open workspacesget_workspace_id- Get the current workspace IDget_environment_id- Get the current environment IDcopy_to_clipboard- Copy text to the system clipboardshow_toast- Show a toast notification in Yaak
Environment Selection
send_http_request accepts an optional environmentId for that execution. It overrides the
active environment without changing the selection in Yaak. Omitting it uses the host's
active environment, as before.
The ID must identify a base or sub-environment in the request's workspace. An empty,
unknown, foreign-workspace, or folder environment ID returns an error before sending.
Use the base environment ID to run with global variables only. Folder variables still
apply according to the request's folder hierarchy.
{
"id": "rq_example",
"workspaceId": "wk_example",
"environmentId": "ev_staging"
}This requires a Yaak host with support for the environmentId plugin API field;
older hosts ignore it. get_environment_id reports the active selection, not the
override used by a previous send.
Overview (`plugins/action-copy-curl/README.md`)
Copy as cUrl
A request action plugin for Yaak that converts HTTP requests into curl
commands, making it easy to share, debug, and execute requests outside Yaak.
Overview
This plugin adds a 'Copy as Curl' action to HTTP requests, converting any request into its
equivalent curl command. This is useful for debugging, sharing requests with team members,
and executing requests in terminal environments where curl is available.
How It Works
The plugin analyzes the given HTTP request and generates a properly formatted curl command
that includes:
- HTTP method (GET, POST, PUT, DELETE, etc.)
- Request URL with query parameters
- Headers (including authentication headers)
- Request body (for POST, PUT, PATCH requests)
- Authentication credentials
Usage
- Configure an HTTP request as usual in Yaak
- Right-click on the request in the sidebar
- Select 'Copy as Curl'
- The command is copied to your clipboard
- Share or execute the command
Generated Curl Examples
Simple GET Request
curl -X GET 'https://api.example.com/users' \
--header 'Accept: application/json'POST Request with JSON Data
curl -X POST 'https://api.example.com/users' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"name": "John Doe",
"email": "[email protected]"
}'Request with Multi-part Form Data
curl -X POST 'yaak.app' \
--header 'Content-Type: multipart/form-data' \
--form 'hello=world' \
--form file=@/path/to/file.jsonRequest with Authentication
curl -X GET 'https://api.example.com/protected' \
--user 'username:password'Overview (`plugins/action-copy-grpcurl/README.md`)
Copy as gRPCurl
An HTTP request action plugin that converts gRPC requests
into gRPCurl commands, enabling easy sharing,
debugging, and execution of gRPC calls outside Yaak.
Overview
This plugin adds a "Copy as gRPCurl" action to gRPC requests, converting any gRPC request
into its equivalent executable command. This is useful for debugging gRPC services,
sharing requests with team members, or executing gRPC calls in terminal environments wheregrpcurl is available.
How It Works
The plugin analyzes your gRPC request configuration and generates a properly formattedgrpcurl command that includes:
- gRPC service and method names
- Server address and port
- Request message data (JSON format)
- Metadata (headers)
- Authentication credentials
- Protocol buffer definitions
Usage
- Configure a gRPC request as usual in Yaak
- Right-click on the request sidebar item
- Select "Copy as gRPCurl" from the available actions
- The command is copied to your clipboard
- Share or execute the command
Generated gRPCurl Examples
Simple Unary Call
grpcurl -plaintext \
-d '{"name": "John Doe"}' \
localhost:9090 \
user.UserService/GetUserCall with Metadata
grpcurl -plaintext \
-H "authorization: Bearer my-token" \
-H "x-api-version: v1" \
-d '{"user_id": "12345"}' \
api.example.com:443 \
user.UserService/GetUserProfileCall with TLS
grpcurl \
-d '{"query": "search term"}' \
secure-api.example.com:443 \
search.SearchService/SearchCall with Proto Files
grpcurl -import-path /path/to/protos \
-proto /other/path/to/user.proto \
-d '{"email": "[email protected]"}' \
localhost:9090 \
user.UserService/CreateUserOverview (`plugins/auth-aws/README.md`)
AWS Signature Version 4 Auth
A plugin for authenticating AWS-compatible requests using the
AWS Signature Version 4 signing process.
This enables secure, signed requests to AWS services (or any S3-compatible APIs like
Cloudflare R2).
Overview
This plugin provides AWS Signature authentication for API requests in Yaak. SigV4 is used
by nearly all AWS APIs to verify the authenticity and integrity of requests using
cryptographic signatures.
With this plugin, you can securely sign requests to AWS services such as S3, STS, Lambda,
API Gateway, DynamoDB, and more. You can also authenticate against S3-compatible services
like Cloudflare R2, MinIO, or Wasabi.
How AWS Signature Version 4 Works
SigV4 signs requests by creating a hash of key request components (method, URL, headers,
and optionally the payload) using your AWS credentials. The resulting HMAC signature is
added in the Authorization header along with credential scope metadata.
Example header:
Authorization: AWS4-HMAC-SHA256 Credential=AKIAโฆ/20251011/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=abcdef123456โฆEach request must include a timestamp (X-Amz-Date) and may include a session token if
using temporary credentials.
Configuration
The plugin presents the following fields:
- Access Key ID โ Your AWS access key identifier
- Secret Access Key โ The secret associated with the access key
- Session Token (optional) โ Used for temporary or assumed-role credentials (treated as secret)
- Region โ AWS region (e.g.,
us-east-1) - Service โ AWS service identifier (e.g.,
sts,s3,execute-api)
Usage
- Configure a request, folder, or workspace to use AWS SigV4 Authentication
- Enter your AWS credentials and target service/region
- The plugin will automatically sign outgoing requests with valid SigV4 headers
Overview (`plugins/auth-basic/README.md`)
Basic Authentication
A simple Basic Authentication plugin that implements HTTP Basic Auth according
to RFC 7617, enabling secure
authentication with username and password credentials.
Overview
This plugin provides HTTP Basic Authentication support for API requests in Yaak. Basic
Auth is one of the most widely supported authentication methods, making it ideal for APIs
that require simple username/password authentication without the complexity of OAuth
flows.
How Basic Authentication Works
Basic Authentication encodes your username and password credentials using Base64 encoding
and sends them in the Authorization header with each request. The format is:
Authorization: Basic <base64-encoded-credentials>Where `` is the Base64 encoding of username:password.
Configuration
The plugin presents two fields:
- Username: Username or user identifier
- Password: Password or authentication token
Usage
- Configure the request, folder, or workspace to use Basic Authentication
- Enter your username and password in the authentication configuration
- The plugin will automatically add the proper
Authorizationheader to your requests
Troubleshooting
- 401 Unauthorized: Verify your username and password are correct
- 403 Forbidden: Check if your account has the necessary permissions
- Connection Issues: Ensure you're using HTTPS for secure transmission
Overview (`plugins/auth-bearer/README.md`)
Bearer Token Authentication Plugin
A Bearer Token authentication plugin for Yaak that
implements RFC 6750, enabling secure API
access using tokens, API keys, and other bearer credentials.
Overview
This plugin provides Bearer Token authentication support for your API requests in Yaak.
Bearer Token authentication is widely used in modern APIs, especially those following REST
principles and OAuth 2.0 standards. It's the preferred method for APIs that issue access
tokens, API keys, or other bearer credentials.
How Bearer Token Authentication Works
Bearer Token authentication sends your token in the Authorization header with each
request using the Bearer scheme:
Authorization: Bearer <your-token>The token is transmitted as-is without any additional encoding, making it simple and
efficient for API authentication.
Configuration
The plugin requires only one field:
- Token: Your bearer token, access token, API key, or other credential
- Prefix: The prefix to use for the Authorization header, which will be of the
format ""
Usage
- Configure the request, folder, or workspace to use Bearer Authentication
- Enter the token and optional prefix in the authentication configuration
- The plugin will automatically add the proper
Authorizationheader to your requests
Troubleshooting
- 401 Unauthorized: Verify your token is valid and not expired
- 403 Forbidden: Check if your token has the necessary permissions/scopes
- Invalid Token Format: Ensure you're using the complete token without truncation
- Token Expiration: Refresh or regenerate expired tokens
Overview (`plugins/auth-digest/README.md`)
Digest Authentication
An HTTP Digest Authentication plugin that implements
RFC 7616, with fallback to the
older RFC 2617 and
RFC 2069 behaviour that many
servers still speak.
Overview
Digest Authentication proves you know a password without ever putting it on the
wire. The server issues a one-time nonce, and the client answers with a hash
over the credentials, the nonce and the request itself.
How it works
Because the digest is computed over a server-issued nonce, the challenge has to
be fetched before the real request can be signed. On each send, the plugin:
- Sends an unauthenticated probe of the same method and URL, carrying no
headers of its own so it cannot authorize anything - Reads the
WWW-Authenticate: Digest โฆchallenge from the401response - Computes the response hash and returns the
Authorizationheader
Configuration
- Username: Username or user identifier
- Password: Password or authentication token
- Realm (advanced): Only needed when the server offers more than one realm.
Leave it empty and the first challenge the server prefers is used.
Supported challenges
| Feature | Supported |
|---|---|
| Algorithm | MD5, MD5-sess, SHA-256, SHA-256-sess |
qop |
auth, auth-int, and challenges that omit qop (RFC 2069) |
opaque |
Echoed back when the server sends one |
userhash |
Declined with userhash=false |
auth-int is used when the server offers it and Yaak has the request body in
hand. Bodies that are streamed from disk or above the size Yaak passes to auth
plugins aren't available to hash, so those requests use auth instead.
Credentials are normalized to Unicode NFC before hashing, per RFC 7616 ยง4.
Usernames outside ASCII are sent as an RFC 5987 extended value (username*).
Troubleshooting
- Server did not offer Digest authentication: The endpoint answered the probe
with a different scheme, or with noWWW-Authenticateheader at all. Check the
URL, and whether the endpoint requires auth in the first place. - Unsupported Digest algorithm: The server asked for an algorithm this plugin
doesn't implement, such asSHA-512-256. - 401 Unauthorized: Verify the username and password. If the server offers
several realms, set the Realm field to the one your account belongs to.
Overview (`plugins/auth-jwt/README.md`)
JSON Web Token (JWT) Authentication
A JSON Web Token (JWT) authentication
plugin that supports token generation, signing, and automatic header management.
Overview
This plugin provides JWT authentication support for API requests. JWT is a compact,
URL-safe means of representing claims between two parties, commonly used for
authentication and information exchange in modern web applications and APIs.
How JWT Authentication Works
JWT authentication involves creating a signed token containing claims about the user or
application. The token is sent in the Authorization header:
Authorization: Bearer <jwt-token>A JWT consists of three parts separated by dots:
- Header: Contains the token type and signing algorithm
- Payload: Contains the claims (user data, permissions, expiration, etc.)
- Signature: Ensures the token hasn't been tampered with
Usage
- Configure the request, folder, or workspace to use JWT Authentication
- Set up your signing algorithm and secret/key
- Add custom JWT header fields if needed
- Configure the required claims for your JWT payload
- The plugin will generate, sign, and include the JWT in your requests
Common Use Cases
JWT authentication is commonly used for:
- Microservices Authentication: Service-to-service communication
- API Gateway Integration: Authenticating with API gateways
- Single Sign-On (SSO): Sharing authentication across applications
- Stateless Authentication: No server-side session storage required
- Mobile App APIs: Secure authentication for mobile applications
- Third-party Integrations: Authenticating with external services
Troubleshooting
- Invalid Signature: Check your secret/key and algorithm configuration
- Token Expired: Verify expiration time settings
- Invalid Claims: Ensure required claims are properly configured
- Algorithm Mismatch: Verify the algorithm matches what the API expects
- Key Format Issues: Ensure RSA keys are in the correct PEM format
Overview (`plugins/auth-oauth2/README.md`)
OAuth 2.0 Authentication
An OAuth 2.0 authentication plugin that
supports multiple grant types and flows, enabling secure API authentication with OAuth 2.0
providers.
Overview
This plugin implements OAuth 2.0 authentication for requests, supporting the most common
OAuth 2.0 grant types used in modern API integrations. It handles token management,
automatic refresh, and PKCE (Proof Key
for Code Exchange) for enhanced security.
Supported Grant Types
Authorization Code Flow
The most secure and commonly used OAuth 2.0 flow for web applications.
- Standard Authorization Code flow
- Optional PKCE (Proof Key for Code Exchange) for enhanced security
- Supports automatic token refresh
Client Credentials Flow
Ideal for server-to-server authentication where no user interaction is required.
Implicit Flow
Legacy flow for single-page applications (deprecated but still supported):
- Direct access token retrieval
- No refresh token support
- Suitable for legacy integrations
Resource Owner Password Credentials Flow
Direct username/password authentication.
- User credentials are exchanged directly for tokens
- Should only be used with trusted applications
- Supports automatic token refresh
Features
- Automatic Token Management: Handles token storage, expiration, and refresh
automatically - PKCE Support: Enhanced security for Authorization Code flow
- Token Persistence: Stores tokens between sessions
- Flexible Configuration: Supports custom authorization and token endpoints
- Scope Management: Configure required OAuth scopes for your API
- Custom Parameters: Add headers and params to the authorization, token, and refresh
requests, replacing generated entries of the same name - Error Handling: Comprehensive error handling and user feedback
Usage
- Configure the request, folder, or workspace to use OAuth 2.0 Authentication
- Select the appropriate grant type for your use case
- Fill in the required OAuth 2.0 parameters from your API provider
- The plugin will handle the authentication flow and token management automatically
Compatibility
This plugin is compatible with OAuth 2.0 providers including:
- Google APIs
- Microsoft Graph
- GitHub API
- Auth0
- Okta
- And many other OAuth 2.0 compliant services
Overview (`plugins/importer-bruno/README.md`)
Bruno importer
Import Bruno's Single File (YAML) or Bruno Collection (ZIP) exports, or select a collection directory. Both OpenCollection 1.0.0 YAML collections and legacy .bru collections are supported. Individual .bru request files also work. A directory/ZIP must contain one collection root (opencollection.yml, opencollection.yaml, or bruno.json); a wrapper directory in the ZIP is allowed. Use Choose folder for an on-disk collection, rather than selecting its configuration file alone.
The importer consumes the shared file-tree API. It skips .git, node_modules, __MACOSX, and hidden entries. It does not install dependencies, load dotenv files, or run collection scripts.
Supported:
- Nested folders, ordering, descriptions, collection/folder headers, and variables.
- Environments, disabled variables, typed values, and secret placeholders. Environment inheritance is flattened into each selectable environment because Yaak does not have nested selectable environments.
- HTTP URLs, query/path parameters, headers, raw bodies, URL-encoded forms, multipart files, and selected binary files.
- GraphQL query and variables in Yaak's sendable body shape.
- gRPC URL, service/method, metadata, and selected message; WebSocket URL, headers, and selected message.
- Explicit auth inheritance, no auth, basic, bearer, digest, NTLM, API key, AWS credentials, common OAuth 1.0 header signing, and OAuth 2.0 flows with header tokens.
- Plain
{{variable}}references converted to Yaak templates. Expressions, dynamic variables, andprocess.envreferences stay unchanged.
Scripts, assertions, actions, request-scoped variables, examples, advanced settings, and unsupported authentication configurations are retained in resource descriptions for manual migration, not executed. Alternative bodies/messages use the selected variant (or the first when unspecified); retained source variants remain in descriptions. File paths are preserved without reading files; relative paths and gRPC proto files may need to be configured after import. External secret providers, certificates, proxies, OAuth token caches, and WebSocket binary messages are not activated by the importer.
OpenCollection does not export persistent resource IDs, so Yaak derives the import source keys using its existing fallback matching.
Implementation references:
Legacy .bru parsing uses Bruno's official @usebruno/filestore (backed by @usebruno/lang). Its YAML serializers normalize parsed collections to OpenCollection before they enter the same Yaak conversion path. YAML collections are assembled directly to preserve their fields.
Overview (`crates/common/yaak-rpc-schema/README.md`)
yaak-rpc-schema
The wire schema for the app's RPC surface: every command name, its request
payload, and its response type, declared once.
Every host that serves the Yaak UI โ the desktop app today, the browser bridge
and anything after it โ imports these types and implements the commands against
them. That is what keeps a request's shape from drifting between hosts, and it
is why the TypeScript bindings (bindings/gen_rpc.ts, exposed to the frontend
as @yaakapp-internal/rpc-schema) are generated from one place.
Nothing here depends on Tauri or on any host. Request structs are plain data,
and so are the few response types declared here rather than in an engine crate.
Command bodies live with the host that runs them.
Adding a command
- Add its request struct and an entry in
with_commands!insrc/lib.rs. - Write the adapter in each host โ the desktop's live in
crates-tauri/yaak-app-client/src/rpc_ext.rs. A host that does not support
the command still has to say so; a missing adapter fails to compile. - Regenerate the bindings:
cargo test -p yaak-rpc-schemawritesbindings/gen_rpc.ts, which is committed.
How hosts consume the list
with_commands! takes the name of a macro_rules! macro and calls it with the
full name(Req) -> Res list. Each host writes a small macro that receives that
list and builds its router:
macro_rules! register_commands {
( $( $name:ident ( $req:ty ) -> $res:ty ),* $(,)? ) => {
pub fn build_router() -> RpcRouter<MyCtx> {
let mut router = RpcRouter::new();
$( router.register(stringify!($name), rpc_handler_async!($name)); )*
router
}
};
}
yaak_rpc_schema::with_commands!(register_commands);The schema decides what commands exist; the host decides how each one runs.
Overview (`packages/platform/src/web/README.md`)
The browser host
Yaak running in a plain tab: no install, no local process. The desktop's own
model layer โ yaak-models, SQLite included โ runs compiled to wasm inside a
worker the tab talks to, so a browser stores exactly what a desktop install
stores, migrations and all.
Select it at build time. vp run web:dev does that and starts the send server
alongside the frontend:
vp run web:devThe flag resolves @yaakapp-internal/platform to ../index.web.ts, which
installs this host instead of the Tauri one. It is a separate entry rather than
a branch inside index.ts so that a web build never pulls @tauri-apps/* into
the module graph at all โ a folded branch would drop the code but keep the
imports it guarded.
Desktop builds are untouched: without the flag, packages/platform/src/index.ts
installs the Tauri host exactly as before.
How it fits together
tab (index.ts, commands.ts) โโMessagePortโโโถ worker.ts โโโถ @yaakapp-internal/web (wasm)
โ โโโ model_writes โโ crates/yaak-wasm โ yaak-models โ SQLite
โ โโ pages in IndexedDB
โโโ send.ts โโPOST rendered requestโโโถ yaak-web (crates-server) โโโถ the internet
โโโ NDJSON: events, response, body, cookies โโ| File | What it is |
|---|---|
index.ts |
The Platform implementation. |
commands.ts |
The command table: model commands forward to the worker; the rest is fixed answers and refusals-with-a-reason. |
connection.ts |
A tab's end of the wire: request/response over a MessagePort, event delivery, and the tab's identity (label). |
send.ts |
Sending: the worker renders (prepare_http_send), the server executes, this file stores what comes back where the desktop stores it. |
server.ts |
Where the Yaak server is, and the wire shapes it speaks (generated from crates-server/yaak-web/src/wire.rs). |
worker.ts |
The process that owns the database. Loads the wasm, opens the DB once, answers each port, fans model_writes out to every port. |
protocol.ts |
The message shapes both sides import. |
errors.ts |
UnsupportedCommandError, the structured refusal. |
storage.ts |
navigator.storage.persist(). |
The Rust side is crates/yaak-wasm (@yaakapp-internal/wasm): boot(),rpc(cmd, payload, label) returning { result, events }, blob get/put, andprepare_http_send(payload) โ the database half of a send (environment chain,
inherited headers and auth, request settings, cookie jar, rendering), which isyaak_models::render::render_http_request, the same function the desktop
renders with.
Its pkg/ is committed; rebuilding needs a clang with a WebAssembly backend
(brew install llvm), and build-wasm.cjs skips with a notice when there
isn't one, so a desktop npm run bootstrap never depends on it.
Behaviours worth knowing before changing anything:
- The worker is a
SharedWorker, and only that. The browser hands every
tab on the origin the same one, which is what makes "one database owner"
true without anyone coordinating โ and makes the browser look like the
desktop: one process holds the data, every window talks to it, it pushes
writes to all of them. It still takes a Web Lock before opening, for the one
overlap the browser doesn't rule out (a reloading tab's dying predecessor).
There is deliberately no fallback to a per-tab worker: two kinds of worker
that can both come up is a race. Every current browser, desktop and mobile,
hasSharedWorkerand Web Locks (Chrome for Android since 148, April 2026);
older ones get a clear "unsupported browser" message rather than a second
SQLite over the same pages. - Every write is stamped with the calling tab's
labelasUpdateSource::Window, exactly like a desktop window label, so the frontend
store's echo handling is unchanged. - Cascade rules, duplicate naming, id generation, serde defaults, and the
lazy first-run bootstrap are all the Rust code's. Nothing about what a
model is is decided in TypeScript. - Persistence is
relaxed-idb: SQLite pages live in IndexedDB, writes land
in memory and flush shortly after. A tab closing mid-flush loses at most the
last few writes.
Commands
109 commands are declared in @yaakapp-internal/rpc-schema. This host answers
32, declines 43 by name with a reason, and refuses the remaining 34 generically.
Implemented (32)
| Group | Commands |
|---|---|
| Models | models_workspace_models, models_upsert, models_delete, models_duplicate, models_get_settings, models_get_graphql_introspection, models_upsert_graphql_introspection, models_grpc_events, models_websocket_events |
| Sending | cmd_send_http_request (through the Yaak server; see below) |
| App | cmd_metadata, cmd_get_workspace_meta, cmd_default_headers, cmd_get_themes, cmd_check_for_updates, cmd_dismiss_notification, cmd_plugin_init_errors |
| Bodies | cmd_http_response_body, cmd_http_response_body_path, cmd_http_request_body, cmd_get_http_response_events, cmd_get_sse_events |
| Plugin surfaces (empty results) | cmd_http_request_actions, cmd_websocket_request_actions, cmd_grpc_request_actions, cmd_workspace_actions, cmd_folder_actions, cmd_template_function_summaries, cmd_get_http_authentication_summaries, cmd_get_http_authentication_config |
| Text | cmd_format_json, cmd_render_template |
Some of these answer honestly rather than fully, and the difference matters:
cmd_render_templatereturns the template unrendered. Resolving variables
and calling template functions is plugin work. The preview shows the raw${[โฆ]}rather than a wrong value.cmd_get_http_authentication_summariesreturns the auth methods Yaak ships as
plugins, so the picker is truthful about the product โ butcmd_get_http_authentication_configreturns an empty form, because the plugin
that defines the form isn't running.cmd_template_function_summariesreturns one provider contributing no
functions. Both summary commands are polled every second until they return
something, so an empty list is a poll that never stops rather than a quiet no.cmd_metadatareports empty strings for the data, log, plugin and project
directories. There is no filesystem behind this host.
Declined by name (43)
Each returns an UnsupportedCommandError carrying cmd, a user-facingmessage, and the capability a caller should have checked. The UI turns it
into a toast.
| Reason | Commands |
|---|---|
| Sending, the parts not wired yet | cmd_send_ephemeral_request, cmd_delete_send_history, cmd_delete_all_http_responses, cmd_import_url |
| No plugin runtime | cmd_reload_plugins, cmd_plugin_info, cmd_plugins_search, cmd_plugins_install, cmd_plugins_install_from_directory, cmd_plugins_uninstall, cmd_plugins_updates, cmd_plugins_update_all, cmd_template_function_config, cmd_template_tokens_to_string, cmd_call_http_request_action, cmd_call_websocket_request_action, cmd_call_grpc_request_action, cmd_call_workspace_action, cmd_call_folder_action, cmd_call_http_authentication_action, cmd_curl_to_request, cmd_format_graphql |
| No filesystem | cmd_import_data, cmd_export_data, cmd_save_response, cmd_save_base64_to_binary |
| Needs a real socket | cmd_grpc_reflect, cmd_grpc_go, cmd_delete_all_grpc_connections, cmd_ws_connect, cmd_ws_send, cmd_ws_close, cmd_ws_delete_connections |
| Workspace encryption | cmd_enable_encryption, cmd_disable_encryption, cmd_reveal_workspace_key, cmd_set_workspace_key, cmd_secure_template, cmd_decrypt_template |
| One tab, no windows | cmd_new_child_window, cmd_new_main_window, cmd_restart |
| Other | cmd_send_feedback |
Refused generically (34)
The 30 cmd_git_* commands and cmd_sync_calculate, cmd_sync_calculate_fs,cmd_sync_apply, cmd_sync_watch. Nothing in the app reaches them unless a
workspace has a sync directory, which a browser tab cannot set.
Anything added to the schema later also lands here, and the error names the
command โ an unlisted command is a gap in commands.ts, and whoever hits it
should be able to see which.
Capabilities
Reported honestly, so callers gate on the question rather than on the host:
| True | False |
|---|---|
httpSending, timeline, cookieJar |
grpc, websocket, git, sync, tlsOptions, localFiles, multiWindow, windowChrome, interfaceZoom, plugins, encryption, updater, clipboardRead, systemFonts, license |
interfaceZoom: false leaves Cmd/Ctrl +, - and 0 to the browser instead
of swallowing them, and drops those three rows from the hotkeys screen.
multiWindow: false means the host cannot open a second window on demand โ
what cmd_new_child_window does for Settings and workspace switching. It is not
a claim that nothing else is looking: other tabs may well be open on the same
worker, and it pushes every write to all of them regardless.
windowChrome: false means the browser owns the frame around the page, so the
header draws no window controls and reserves no room for macOS traffic lights.
Multiple tabs
Each tab mints a label at load (tab_xxxxxxxx) and sends it with every command;
the worker stamps writes with it as UpdateSource::Window { label }, standing in
for the desktop's window label. The worker fans each write out to every
connected tab, and the receiving tab's store applies or ignores it exactly as a
desktop window would.
The label is deliberately not kept in sessionStorage: duplicating a tab
copies session storage, and two tabs sharing one identity would each mistake the
other's writes for an echo of their own and drop them.
Known gaps
- Storage persistence is requested, not guaranteed.
navigator.storage.persist()
runs at boot; browsers grant it on their own heuristics and often decline onlocalhost. pkg/yaak_web_bg.wasmis 3.8 MB and committed (nowasm-opt, matchingyaak-templates). It will churn on every model-layer change; a CI-built
artifact is the real answer.- Settings opens in the same tab and is left with the browser's Back button.
- Settings shows Data Directory / Logs Directory rows with empty values; the
Create Workspace dialog offers directory sync and encryption. Should be gated
onlocalFiles/sync/encryption. cmd_render_templatereturns the template unrendered. Resolving variables
and calling template functions is plugin work.- A declined command logs an unhandled rejection next to its toast โ the
app's owncreateFastMutation.mutate, same on desktop. yaak-rpc-schemadoes not come to wasm (it pulls the git/gRPC/plugin
crates for their types), so the crate declares the handful of request shapes
it needs locally, andcommands.tsstays typed againstRpcSchema.
Sending
A page cannot see a response the way a desktop app can โ CORS exposes a handful
of headers, redirects are followed silently, there is no timeline โ so the
network half of a send runs on a small stateless server,crates-server/yaak-web. This layer stays the only place data lives:
send.tscreates thehttp_responserow (stateinitialized), as the
desktop does, so anything that goes wrong lands in the response pane.- The worker resolves and renders the request (
prepare_http_send): the
environment chain, inherited headers and auth, request settings, the cookie
jar. This is the desktop'sHttpSendInputs, in Rust, on the same model layer,
withyaak_models::render::render_http_request. Variables (${[ name ]})
render here with no plugins involved. - The rendered request, the settings and the jar's cookies are POSTed to the
server. It streams back timeline events, the response head, body chunks and a
terminal frame carrying the jar as the send left it. - Each frame is written where the desktop writes it: the response row as it
progresses,http_response_eventrows for the timeline (which is whytimelineis true), the body under the response id viablob_put, and the
cookie jar throughmodels_upsert. Every write fans out to every tab.
What sends today: any saved request whose templates are variables and whose
authentication is none, or an inline header. Sending a request that needs a
template function (${[ timestamp() ]}) or an authentication plugin (bearer,
basic, OAuth, โฆ) is refused before anything leaves the tab, with a message naming
what it needs; those light up when plugins run in the browser. Requests with a
file body or multipart file fields are refused by the server (it has no access to
your files, and must not read its own). And on a public instance a request tolocalhost or a LAN address can't work: the server runs elsewhere and refuses
private ranges outright โ that is what the desktop app is for. A self-hosted
server on your own network can be started with --allow-private-networks, which
is the one case where those addresses are the user's to reach.
Where the tab sends (server.ts): its own origin, in development as well as
production, so there is no address to learn and no CORS in the loop. A
production build is served by the sender itself (yaak-web --serve dist/apps/yaak-client, which is what the ghcr.io/mountain-loop/yaak-web
image runs). In development the Vite server passes /v1 through to yaak-web
instead, following PORT to find it โ vp run web:dev starts both.VITE_YAAK_WEB_URL overrides this, for a deployment that keeps the app and the
server apart.
--- METRICS ---
- Files Extracted: 19
- Estimated Token Budget: ~18339 tokens
- Canonical Reference: https://codewiki.google/github.com/mountain-loop/yaak