Skip to content

Settings

Tony West edited this page Aug 28, 2026 · 8 revisions

Settings

The Settings tab is organized into seven categories in a sidebar: General, Appearance, Project, Testing Browser, Plugins, Automation, and Webhooks. Changes take effect immediately.

The split follows what the setting belongs to. General and Appearance are yours and follow you across engagements. Project holds everything specific to the engagement you are working, and is saved into the project file — see Projects. Plugins, Automation and Webhooks are none of those: all three are tied to the machine. Capability tokens, installed automations and webhook endpoints are stored apart from both configs, so a grant set, a piece of executable code or a signing key never travels with an exported project or a saved snapshot.

Proxy

Under General.

  • Max requests: size of the in-memory ring buffer backing History. Default is 5000. Older entries are evicted once the buffer fills.
  • Auto-forward (s): how long a paused request or response waits before Joro forwards it automatically so the client does not hang. Default is 60. Enable or disable the two intercept phases themselves from the Intercept tab.
  • SOCKS upstream: route Joro's outbound traffic through an upstream SOCKS proxy. Fill in Host and Port, optionally User and Password, and toggle DNS over SOCKS to tunnel name resolution through the proxy as well. Useful for sending engagement traffic through a corporate egress, a remote host, or any other proxy chain.

Click Save to apply changes in this group.

Connection

Under General.

  • Default to HTTP/2: advertise h2 to the browser and forward upstream as HTTP/2 where supported. Turn off to force HTTP/1.1 everywhere, for compatibility or predictable framing. Takes effect on new connections.
  • HTTP/1 keep-alive: reuse upstream HTTP/1.1 connections across requests. Turn off to force one connection per request.

Server

Under General. Shows the proxy port, the UI port, and the running version, and controls updates.

Joro checks for a newer release at startup and periodically in the background, and surfaces an update prompt when one is available. Toggle Update checks off here, or pass --disable-update-checks at launch. Check now runs a check immediately.

When you apply an update, Joro upgrades itself in place and re-execs:

  • A binary installed from a release downloads the matching release archive from GitHub and swaps itself out.
  • A binary running from a git checkout runs git pull && make build.

SettingsPluginsManage exposes a Restart Now button that uses the same re-exec, which you also need after adding or removing a plugin.

Update checks run in proxy mode only. A Listener-Mode or Team-Server instance never checks for or applies updates — upgrade those hosts manually.

Appearance

  • Theme: pick from the available themes. Stored in your browser and applied on page load.

  • Visible tabs: uncheck any tab you do not use to hide it from the header navigation. Top-level tabs added by a tab-type plugin appear here too. Plugin management itself is not a tab and is not listed — it is a category of this page.

  • Dashboard layout: choose which widgets appear where on the Dashboard, separately for solo and team sessions.

  • Streamer mode: replaces identifying values with black bars so you can demo or stream without exposing your setup. Toggle it from the crossed-out eye in the header, from here, or with Ctrl/Cmd+Shift+. — the header icon lights up while it is on. The shortcut is ignored while a text field or a code editor has focus, so it never fires mid-sentence. Stored in your browser like the theme, so it survives a reload and never travels to teammates in a project or shared config.

    What it bars is your infrastructure and the engagement's configuration: the network graph's node labels and tooltips, proxy health, connection and listener config, project and operator names, callback tokens, probe IDs, payload URLs and domains, C2 addresses and the pasted Sliver operator config, the connected Sliver session and Mythic callback names, the auth key on Generate, automation token names and one-time secrets, the principal column in the Automation Activity log, the MCP endpoint, the SOCKS upstream host and user, a command automation's command line, and local file paths. It also covers scope rules, noise filter patterns, and Match & Replace rules. Those describe the target rather than your setup, but a scope list read off a stream tells a viewer exactly what you are engaged to test.

    Captured traffic is not covered: History, Map, Detect, the raw request and response viewers, rendered responses, and plugin tabs keep their real values. Redaction is applied when a value is drawn on screen, so it guards against screen capture and shoulder-surfing, not storage — the API, saved projects, and Dead-Drop exports are unchanged, and a Copy button still puts the real value on your clipboard. That last point matters for a one-time secret such as a new automation token: you can copy it while it is barred, but you cannot read it. A value is barred as it is drawn, so a toast or an error message already on screen when you flip the switch keeps its real text — turn streamer mode on before you start sharing, not after.

Project

The Project category is the engagement's home. It holds:

  • The project browser — create, switch, import, and save projects, with per-project auto-save and save-history settings. See Projects.
  • The Team Server card, for connecting to a remote listener or team server.
  • The Team Configs panel, for publishing and loading project configurations across a team.
  • A Filtering card with three sub-tabs: Scope, Noise Filter, and Customize Requests.

Team Server

Connect this proxy instance to a remote listener or team server running on a remote host.

  • Listener URL: the base URL of the listener's API, for example http://<listener-ip>:9090. Once set, the Interact tab pulls tokens, probes, and interactions from this listener.
  • Team Token: the 32-character hex token printed when the team server started. Required for team server mode.
  • Nickname: the name other operators see in chat, shared Notes, and the operator roster.

A status row shows the current connection state and any error the team server returned. Clear these fields to disconnect.

See Listener-Mode and Team-Server for standing up the remote side.

Team Configs

Appears once a Listener URL is set. Publish your current project to the team under a name, or load a configuration a teammate published. Loading writes a new local project and switches to it rather than overwriting anything you have. See Team-Server.

Scope

A two-level allow/deny filter that decides which requests go through full interception and capture versus being tunneled or forwarded raw.

  • Level 1 (CONNECT): when the browser opens an HTTPS tunnel, Joro checks the target host. Out-of-scope hosts are tunneled raw without MITM, so TLS is never decrypted for them.
  • Level 2 (request): after TLS termination, each individual request is checked against host plus method plus path. Out-of-scope requests are forwarded without capture or intercept.

Each rule has a host pattern, optional HTTP methods, an optional path pattern, and an include/exclude flag. Exclude rules override include rules.

Scope is disabled by default, which means every request is captured. When scope is enabled with no rules, nothing matches, so everything is blocked. This is a deliberately safe default.

Scope also gates Detect by default, so narrowing scope narrows what gets scanned. It is the authorization control for automation as well: a capability token that is held to scope has every send refused while scope is off or has no rules — see Automation.

Importing scope rules

Rules can be loaded from a file instead of typed in. Both controls sit on the Rules caption row of the Scope sub-tab:

  • Download example scope file saves joro-scope-example.json, a two-rule file to edit as a starting point. Two rules rather than one for the same site, because a leading *. still requires a label: *.example.com covers subdomains but not the apex, so both are listed.
  • Import… picks a file to load.

The format is JSON, the same shape a project already stores:

{
  "scopeEnabled": true,
  "scopeRules": [
    { "pattern": "example.com", "methods": [], "path": "", "include": true },
    { "pattern": "*.example.com", "methods": ["GET", "POST"], "path": "/api/*", "include": true }
  ]
}

Burp XML or JSON exports and plain host lists are not accepted. pattern is a host glob with no scheme and no path — the path belongs in path, and a pattern containing a slash is rejected with a message saying so. There is no CIDR support; an address range has to be written as a glob.

Picking a file applies nothing on its own. A summary bar reports how many rules were found and how they split between include and exclude, then waits:

  • Merge, the default, keeps your existing rules and skips duplicates.
  • Replace drops every existing rule for the ones in the file, and says how many it will remove.

Duplicates collapse in both modes, compared on pattern, path, methods, and the include flag after normalization — patterns are lowercased and methods uppercased, so a rule differing only in case is still a duplicate. The result line reports how many were imported and how many were skipped.

scopeEnabled is applied only when the file carries it. Replace takes the file's value outright; merge can turn scope on but never off.

A file may hold up to 1000 rules and 1024 KB, and every rule must state include explicitly — an omitted flag would silently invert the rule's meaning. Validation is all-or-nothing: the first bad rule aborts the import naming which one it was, and nothing is applied.

Note this is not the same Import as the one above the project table, which takes a whole .joro file. Handing a project file to the scope importer tells you as much rather than failing on the JSON.

Noise Filter

Silently forwards common browser background traffic without capturing or decrypting it. Typical entries cover captive portal checks, telemetry endpoints, OCSP revocation lookups, safe-browsing services, and Mozilla's Oblivious HTTP relays.

  • Enabled by default with a curated pattern list.
  • Patterns can be added, removed, or the whole filter disabled.
  • Runs before scope. Even if a noisy host matches a scope include rule, it is still tunneled silently and never goes through MITM, which keeps History clean.

Add hosts to the noise filter when they appear repeatedly and are never interesting. Use scope to define what you are actually testing.

Note: projects saved before a default pattern existed have it added when you load them, but only patterns that are missing entirely. Defaults you deliberately deleted stay deleted.

Customize Requests

A sub-tab that holds two related features: Match & Replace (rewrite rules) and Custom Data (additive injection). Both run automatically on in-scope traffic and can be toggled independently.

Match & Replace

Rewrites parts of requests and responses based on pattern rules. Each rule has:

  • Target: one of request_header, request_body, response_header, response_body, or ws_message.
  • Match type: string (literal) or regex.
  • Match: the pattern to search for.
  • Replace: the replacement text.

Rules run cumulatively in order. A response body rule sees the output of any earlier response body rule. Only complete text or binary WebSocket messages are rewritten; control frames pass through untouched.

Match & Replace runs before Intercept pauses a message, so what you see in the intercept editor is the rewritten bytes.

Custom Data

Custom Data is purely additive. It adds fixed headers, query parameters, or body snippets to every in-scope request. There is no match pattern.

  • Header: adds a constant HTTP header (for example, X-Forwarded-For for testing IP-based logic).
  • Query parameter: appends ?name=value or &name=value to the URL.
  • Body: appends text to the request body when the body is a form or compatible format.

Custom Data runs after Match & Replace in the pipeline, so any addition you configure here sits on top of the rewrites you have already applied.

Testing Browser

Launch a browser already routed through the proxy with Joro's CA trusted, using a separate profile per project. Set an optional Landing URL, clear the current project's cookies, open the Setup Check wizard, or Download CA to trust the certificate somewhere else.

A status line reports which browser was detected, or warns when none was. See Testing-Browser.

The CA is generated on first run and reused across restarts. It lives at ~/.joro/ca.crt (ECDSA P-256, valid for 10 years).

Plugins

Plugin management lives here rather than on a tab of its own. The category opens on Manage, which lists every plugin, uploads a built .so or .dylib, deletes one, and carries the Restart Now button that adding or removing a plugin requires. Files that failed to load are listed alongside the working ones, with the reason on hover, so a plugin built against the wrong toolchain can be removed from the same table.

Beside Manage sits one sub-tab per loaded feature plugin, each serving its own embedded UI. A tab-type plugin is different: it adds a genuine top-level tab to the header, not a sub-tab here.

See Plugins for the plugin types, how to build one, and what a plugin can reach.

Automation

The Automation category issues capability tokens, runs Joro's MCP server, and holds the automations you have installed. It has three sub-tabs — what is installed and what wakes it, how the surface is configured and who may reach it, and what has happened:

  • Scripting — everything you author, in one left rail. Automations (of either kind: a JavaScript script or a local command), Lenses (the automations that render a tab beside Raw and Render), Triggers (the custom ones you build), and a collapsed Built-in events group listing Joro's own. Selecting a row opens it in the editor beside the rail; New ▾ offers Script, Lens, Local command and Trigger. See Scripting.
  • Settings — the MCP server card with its Enabled toggle and Port, the token table and its editor, and the Run budget: four cards covering what a script run gets (Per run), what this machine allows scripts (This Joro), and the same pair for command automations (Per command run, This Joro (commands)). Each budget field shows Joro's default, the most a run may ask for, and any figure you cannot exceed.
  • Activity — every capability call, and beneath it the log of script runs.

The server is off by default, binds loopback only, and refuses to start until a token exists. Tokens are stored in ~/.joro/automation.json, installed automations under ~/.joro/automations/, and custom triggers in ~/.joro/triggers.json — none of them in a config, because a config is published to teammates.

Automation is proxy mode only, and the whole category collapses to a notice when Joro was launched with --no-automation. The Scripting sub-tab additionally requires at least one of --automation-scripting or --automation-commands, and names both when neither is present — with only --automation-commands you get a working editor, list and run log, and the Kind dropdown simply refuses the script half. The two command cards on Settings are marked not enabled until Joro is started with --automation-commands. See Automation for the capability catalog, the token profiles, and what a grant does and does not permit, and Scripting for the two kinds of automation and every budget field.

Custom triggers are the exception: they are served whether or not any automation flag is set, which is what lets a webhook be filtered on a Joro started with none of them.

Webhooks

The Webhooks category holds the outbound endpoints Joro posts to when something happens — a finding, a response matching a filter, a campaign finishing, an automation completing. A rail of configured endpoints sits beside the one being edited.

It watches the same triggers an automation does, so a filter built under AutomationScripting decides both. It needs none of the automation flags: an engagement that wants a finding in a team channel does not have to arm an agent to get one.

Endpoints and their secrets live in ~/.joro/webhooks.json, mode 0600, never in a config. Secrets are never returned to the browser, and a stored one is kept when the field comes back empty. Proxy mode only, and the whole category collapses to a notice when Joro was launched with --no-webhooks. See Webhooks.

Configurations

Joro separates what belongs to you from what belongs to the engagement.

User Config is your machine-level preferences: theme, hidden tabs, dashboard layout, SOCKS proxy settings, HTTP/2 and keep-alive, auto-forward timeout, max requests, and any user-scoped state from installed plugins such as API keys or personal tokens. Save and restore named snapshots from GeneralUser Config.

Project Config is the engagement: scope rules, noise patterns, match-and-replace rules, custom data items, notes, highlights, captured history, detection settings and findings, listener URL and team credentials, and any project-scoped plugin state such as Interactsh servers with their correlation IDs. Projects are managed from the Project category and saved as .joro files — see Projects.

Automation tokens are in neither. They live in ~/.joro/automation.json, at mode 0600, along with the MCP listener's enabled state and port. A project config is published to teammates, so a grant set and an auto-starting listener riding along would hand over real privilege; a user config round-trips through save, load and export and backfills new defaults by version, which is not behavior security state should inherit. See Automation.

Installed automations are in neither either, for the same reason one level up: a project config is published to teammates, and shipping executable code that runs against the full automation SDK is the same transfer of privilege. They live under ~/.joro/automations/, and are machine-global along with whether each one is enabled. What an automation stores through joro.storage does ride in the project config, as an opaque blob, and resets when you switch projects. See Scripting.

Loading a config on a machine that does not have one of the referenced plugins installed preserves that plugin's state blob for next time; nothing is silently dropped, and a notice tells you which plugins were affected.

When you are on a team server, a project configuration can also be published to the team and loaded by other operators. See Team-Server.

Clone this wiki locally