The settings file
Keep Gatesys SSH preferences, AI providers, hooks, command-risk rules and shell profiles in ~/.gatesys-ssh/settings.json, checked by a JSON schema.
Gatesys SSH keeps your preferences, your AI providers and your hooks in one JSON file you can read, edit and keep in a dotfiles repository. Changes in Settings are written to it, and edits you make to it apply while the app runs.
Where it is
| System | Folder |
|---|---|
| macOS and Linux | ~/.gatesys-ssh/ |
| Windows | %USERPROFILE%\.gatesys-ssh\ |
Every page of Settings shows the path under Saved automatically to, with Open settings.json and Reveal folder beside it.
The folder holds:
| File or folder | What it is |
|---|---|
settings.json | Preferences, AI providers without their keys, and hooks |
settings.schema.json | The file’s schema, written by the app |
hooks/ | A README, an example receiver (verify-signature.mjs) and a place for command-hook scripts |
logs/ | Where a new file hook writes by default |
account.json | Your Gatesys account’s name, email and plan. See Sign in and licence |
license.json | The licence on this device and its last check |
farabi/system.md | Farabi’s instructions, the layer below its safety core. See Instructions and skills |
skills/ | Your Farabi skills, one folder each with a SKILL.md. See The SKILL.md file |
The folder is readable only by you, and so are its files. Hosts, trusted host keys, snippets, the audit log, host facts and the vault are not here: they stay in the app’s own data folder.
What the file holds
{
"$schema": "./settings.schema.json",
"version": 1,
"theme": "system",
"metricsInterval": 5,
"farabiLanguage": "auto",
"hooks": [
{
"name": "Ops receiver",
"kind": "webhook",
"url": "https://hooks.example.com/gatesys",
"groups": ["sessions", "ai"],
"secret": { "env": "GATESYS_HOOK_SECRET" }
}
],
"account": { "apiBase": "https://api.gatesys.ai" },
"setup": { "completedAt": "2026-09-27T10:12:03.120Z" }
}- Preferences are top-level keys, such as
theme,fontSize,scrollback,metricsInterval,aiEnabledandchangeSafetyNetSeconds. A key you leave out takes its default. aiProvidersis the list of AI providers, andaiProviderIdnames the default one. API keys are never here.farabiSkillslists the ids of the Farabi skills on in every conversation, andfarabiAutoSkillssets whether a new panel turns on what a host’s facts suggest. See Instructions and skills.farabiLanguageis the language Farabi writes and asks in:auto(the default, which follows this computer’s locale),en,trorpt. See Farabi’s language.commandRisksholds your own command-risk rules (add), the built-in rule ids you turned off (disable), and built-ins set to another level (lower, such as{"git-reset-hard": "caution"}). See Command risks in settings.json.terminal.localholdsdefaultProfile, the id of the shell a new local terminal opens with (empty for your login shell), andprofiles, your own shells. See Profiles in settings.json.hooksis the list of hooks. A hook you add by hand needs aname, akind(webhook,commandorjsonl), itsgroups, and itsurl,commandorpath.account.apiBaseis the Gatesys account server, andaccount.authorizeUrl, when set, its sign-in page. See Another account server.setup.completedAtrecords when the first-run setup finished. Remove it to see the setup again at the next start.
Edit the file
- Click Open settings.json in Settings, or open the file in any editor.
- Make your change and save.
The app reads the file a moment after you save and applies it straight away, with no restart. VS Code and Cursor read settings.schema.json through the $schema line, so they complete keys and flag wrong values as you type; the app checks every edit against the same schema.
When the app writes the file, it keeps your key order and any keys it does not know, writes through a temporary file so the file is never half-written, and leaves a symlinked settings.json a symlink. If you delete the file, the app writes the settings in use back.
When the file has an error
An edit that does not parse, or does not match the schema, is not applied. A banner across the top of the window says what is wrong and where, for example settings.json was not applied: line 8, column 17, with Open settings.json, and each Settings page says Error on line 8 of settings.json.
- The app keeps using the last good settings.
- It never overwrites the broken file. It is yours to fix. Changes you make in the app meanwhile last until you quit.
- Save a fixed file and the banner goes away on its own.
Secrets stay out
The file is readable by any program you run and often ends up in a repository, so it never holds a secret. An edit that writes one is refused with a banner, like any other error: a value under a key such as password, token, apiKey or secret.
- API keys and passwords go in the app, which keeps them in its vault.
- A webhook’s signing secret is named, never written:
"secret": {"vault": "<id>"}, set by the app, or"secret": {"env": "GATESYS_HOOK_SECRET"}to read it from the environment the app started in.
Command hooks and endpoints need approval
Anything that can write the file could otherwise point the app at a program or a server of its own. So a few things that arrive by an edit wait until you approve them once in the app:
| Added in the file | Waits until | Where to approve |
|---|---|---|
| A command hook | Its program and arguments are approved, and again after any change to them | Settings › Automation › Hooks: the hook reads Waiting for your approval; click Approve |
| A cloud provider endpoint on another machine, such as a gateway, that is not the vendor’s own API | Approved before your API key or a prompt is sent there | Settings › Farabi › Model: Set in settings.json — waiting for your approval for the default provider, or Needs approval in the providers list; click Approve |
An account.apiBase that is not Gatesys’s own, or an account.authorizeUrl | Approved before anything is sent there or your browser is sent to it | Settings › Account: Approve the account server first |
A skill in skills/ that the Injection Shield flags | Approved before any conversation uses it, and again after any change to it | Settings › Farabi › Skills: the skill reads Flagged; click Approve |
A built-in command-risk rule turned off in commandRisks.disable, or lowered in commandRisks.lower | Approved before the rule is weakened; until then it stays as it was | Settings › Safety › Command risks: settings.json weakens a rule; click Approve |
A local shell profile in terminal.local.profiles | Approved before it can open, and again after a change to its program, arguments, folder or environment | Settings › Terminal › Local terminal: the profile shows what it would run; click Approve |
Rules you add to commandRisks, and built-ins you raise, apply at once: they only add protection. A critical rule can never be turned off, or lowered below danger, and the rules that block attacks cannot be weakened at all.
A hook, provider, rule change or shell profile made in Settings needs no extra step. Each approval is kept in the vault as a hash, so a program that edits settings.json cannot approve itself. A hook added by file edit carries a from settings.json tag on its card.
Keep a second profile
Set GATESYS_HOME to a folder before starting the app, and that folder is used in place of ~/.gatesys-ssh: its own settings, hooks, account, licence, Farabi instructions and skills.
GATESYS_HOME="$HOME/.gatesys-ssh-work" "/Applications/Gatesys SSH.app/Contents/MacOS/Gatesys SSH"The variable names the folder itself, not its parent.
Something unclear or wrong? Tell us.