Configuration Guide
Language: 中文 | English
Nearly all of FSAR's runtime behaviour is driven by a single YAML configuration file. This page explains every section — its meaning, defaults, and accepted values.
Where the config lives
| File | Role |
|---|---|
config/fsar.yaml.template |
The read-only template shipped with the repo; lists every option and its default |
~/.fsar/config/fsar.yaml |
Your config, copied from the template on first run |
- On first launch, if
~/.fsar/config/fsar.yamlis missing, FSAR copies the template there. - Afterwards you edit it through the GUI (onboarding wizard, settings pages) or by hand.
- Delete the whole
~/.fsar/directory to reset FSAR to a clean state (config, memory, caches, and logs are all removed).
All defaults below are taken from
config/fsar.yaml.template.
onboarding — wizard state
Tracks the first-run wizard's progress; normally maintained by the UI, no need to edit by hand.
onboarding:
completed: false # whether the wizard is complete
completed_at: null
completed_steps: []
skipped_steps: []
started_at: null
last_step: null # last step visited
agent — agent tier
agent:
tier: medium
tier controls how much reasoning/execution effort the agent loop spends. There are several grades (default medium, up to ultra); higher tiers invest more in tool use and planning.
Note:
agent.tierandreflection.intensitybelow are two unrelated "intensity" dials — do not confuse them.
chat — default model
chat:
default_model:
kind: model
provider: "" # provider name (matches an entry in llm.providers[])
model: "" # model name
The chat model used by new sessions. Leave empty to let the UI choose.
workspace — workspace & output directory
workspace:
output_dir: "" # directory where the agent saves generated files (reports, webpages, images, scripts, exports)
Where the agent saves files it produces. Leave empty to default to ~/FSAR-workspace (a FSAR-workspace folder in the current user's home directory). Even when the agent has full-disk ("all computer") access, it saves generated output into a task-named subfolder here instead of defaulting to the Desktop. Set it to any absolute path you prefer.
security — the safety layer (important)
FSAR's core selling point is defense in depth. This section configures each layer. For the full mechanism, see SECURITY.md at the repo root.
security:
hardline_disabled_classes: [] # disable certain hardline classes (A–I); all on by default
power_user_mode: false # power-user mode (relaxes some confirmations)
custom_sensitive_paths: [] # extra sensitive paths (require confirmation)
always_allow_paths: [] # paths always allowed without confirmation
- hardline_disabled_classes: the hardline guard is an unconditional command floor, split into nine classes A–I (disk destruction, system lifecycle, persistence, privilege escalation, resource exhaustion, service control, network-security config, fetch-and-execute, filesystem integrity). All are enabled by default; do not add entries here unless you fully understand the consequences.
security.skills — skill safety
skills:
review_required: true # require review before running a skill
subprocess_env: # subprocess environment scrubbing
enabled: true
allow: [PATH, HOME, LANG, TMPDIR, SYSTEMROOT, USERPROFILE] # allowlist
strip_prefixes: [API_KEY, TOKEN, SECRET, AUTH] # strip vars containing these
llm_review:
enabled: false # additionally review skills with a small model
subprocess_env ensures that when a skill runs, the child process only sees allowlisted variables, and any variable whose name contains API_KEY/TOKEN/SECRET/AUTH is stripped — provider keys never leak into skill code.
security.mcp — MCP server safety
mcp:
review_required: true # require review before installing/enabling an MCP server
cwd_pinning:
enabled: true # pin the MCP server's working directory
require_dir: true # require an explicit directory
security.egress — network egress control
egress:
enabled: false # off by default; when on, gates skill/command outbound connections
mode: deny # deny = default-deny, only allowlist passes; allow = default-allow, only blocklist blocked
allowlist:
- "api.openai.com:443"
- "api.anthropic.com:443"
- "127.0.0.0/8"
blocklist:
- "*.onion"
- "169.254.0.0/16" # block link-local / metadata addresses
security.redaction — output redaction
redaction:
enabled: true
max_string_length: 16384 # truncate over-long strings to avoid leaks/bloat
patterns: [] # extra redaction patterns
security.memory — memory-write sanitization
memory:
write_sanitization:
enabled: true
block_on_match: true # refuse the write when a rule matches
custom_patterns: []
Prevents secrets and other sensitive content from being written into long-term memory.
security.content_screening — prompt-injection screening
content_screening:
enabled: true # on by default: active on a fresh deployment
threshold: 0.62 # confidence at or above this quarantines the item
scan_on_startup: true # scan existing content in the background at startup
A model decides "normal roleplay / user statement" versus "an instruction aimed at the model". This catches payloads that are only harmful by context and that a keyword regex cannot see. If llm.judge is configured it uses JEV, otherwise the active LLM.
- On write: every memory write (facts, experience, preferences, patterns, task reflections) is screened on a background thread. Conversation is not blocked.
- At startup depends on whether JEV is configured. With
llm.judgeset, every launch re-checks everything — JEV calls are cheap. Without JEV, only items newer than the last scan are checked, and a full pass would otherwise spend your own API budget on every launch. On first run the existing backlog is adopted without being scanned. - On a hit: the item moves from its store into quarantine and appears on the GUI Notifications page, where it can be restored or permanently deleted. Restored content is allowlisted by hash and skipped from then on.
- Character cards are never deleted — only their text fields are blanked, because sessions still reference the card id.
- When screening is unavailable (no model configured, timeout, call failure) nothing is quarantined; the Notifications page instead reports how many items went unscreened this run.
security.visitor_screening — screening what an outside member says
visitor_screening:
enabled: false # off by default — see below
threshold: 0.7 # confidence at or above this bans the credential
Reads every line an outside room member sends, and bans that member's credential when the line tries to steer the model, attacks someone, floods, or reaches for this machine. It is off by default because it can take a credential away on a model's say-so — that is a decision, not a default.
security.file_read_blacklist — file-read blacklist
file_read_blacklist:
enabled: true
defaults: true # default blacklist: ~/.ssh/*, ~/.aws/credentials, ~/.gnupg/*, *.key, *.pem, id_rsa
extra_patterns: [] # additional patterns to forbid reading
Other security switches
session:
no_trust_mode: false # disallow "trust for this session"; confirm every time
small_agent_review:
enabled: false # second-opinion review of high-risk actions by a small model
notifications — the notification feed
notifications:
review:
enabled: true # quarantine notices
release:
enabled: true # newer GitHub releases
include_prerelease: false # widen a stable build to pre-releases
announcement:
enabled: true # official announcements from the repo
Each kind can be switched off independently; the feed's category filter mirrors these three. release.include_prerelease is what turns a stable build into one that also hears about betas.
github — API token
github:
token: "" # optional; raises the Releases API rate limit
The update check and announcement pull work without a token, at GitHub's anonymous rate limit. Set one if you check often or hit the limit.
llm — LLM providers
llm:
active: "" # name of the active provider
providers: [] # list of providers, each with name/provider/base_url/api_key/model, etc.
vision_model: # optional dedicated model for image_analyze / computer use
base_url: "" # empty = use the active chat model
api_key: ""
model: ""
judge: # optional per-candidate relevance judge for memory injection
base_url: "" # empty = disable, and injection falls back to priority order
api_key: ""
model: ""
- Supports OpenAI, Anthropic, Google, DeepSeek, and any OpenAI-compatible endpoint; local models via Ollama / LM Studio.
- A provider row may set
format: responsesto route calls through the OpenAI Responses API (/v1/responses) instead of chat completions. Setting an openai preset's family to "OpenAI Responses" in the GUI writes this automatically. vision_modelis only used when set; otherwise image analysis goes through the active chat model.judgemust speak JEV's protocol — it returns typed judgments rather than text, so an ordinary OpenAI-compatible model cannot be dropped in.base_urlandapi_keyaccept${ENV_VAR}. Configure it from the Models tab in Settings.
tts — text-to-speech
tts:
active: "" # active TTS provider
autoplay: false # automatically read replies aloud
default_voice: ""
providers: []
asr — speech recognition
asr:
active: "" # active ASR provider
language: "" # recognition language (empty = auto)
providers: []
social — social-platform bridge
The same engine can send/receive through Telegram, Feishu (Lark), and WeChat; each platform can independently override the character and user cards.
social:
telegram:
enabled: false
bot_token: ""
feishu:
enabled: false
app_id: ""
app_secret: ""
verification_token: ""
encrypt_key: ""
wechat:
enabled: false
account_id: ""
bot_token: ""
base_url: ""
character_card_id: null # platform-specific character card (null = global)
user_card_id: null
memory — memory & reflection
memory:
short_term_window: 50 # recent messages kept in short-term memory
reflection_interval_hours: 12 # minimum interval for idle-batch reflection (hours)
reflection_intensity: medium # reflection intensity (several grades; default medium)
inject_budget_chars: 2400 # shared character budget for all injected candidates
inject_candidate_cap: 40 # how many candidates the judge may score
inject_score_floor: 0.35 # relevance below this is dropped
inject_max_item_chars: 600 # per-item cap
enable_rating_prompt: true # prompt the user to rate replies
embedder: # semantic embeddings (for semantic memory/recall)
provider: ""
base_url: ""
model: ""
api_key: ""
timeout: 60
Recall is packed by relevance into one shared budget rather than truncated by position, so nothing is cut mid-sentence. The budget sits near the old truncation limit on purpose: the judge only earns its keep when the candidate pool exceeds it. Editable in Settings → Advanced. (This replaced recall_max_chars, which was tuned for positional truncation.)
llm_cache — provider prompt-cache bookkeeping
There is no local response cache. The disk-level L1/L2 cache was removed; what remains is bookkeeping for the provider's own server-side prompt cache (Anthropic cache_control, Gemini cachedContents), whose markers are still applied where the provider supports them.
llm_cache:
retention: short # retention policy for the bookkeeping
use_responses_api: false # legacy override; prefer per-provider format="responses" in llm.providers[]
gui — UI server
gui:
host: 127.0.0.1 # backend listen address (local-only by default)
port: 8765 # browser at http://127.0.0.1:8765
logging
logging:
level: INFO # log level; logs written to ~/.fsar/data/logs/
permissions — tool permissions
permissions:
mode: normal # session mode: strict / normal / trust (affects when confirmation appears)
tools: {} # per-tool trust/ask/deny configuration
path_rules: [] # path rules (a match denies)
mode together with the risk level decides whether a tool call proceeds, needs confirmation, or is denied. See the "Risk engine" section of SECURITY.md.
mcp — MCP server list
mcp:
servers: [] # installed MCP servers (name, command, args, env, etc.)
reflection — reflection triggers
reflection:
intensity: medium # reflection intensity (4 grades; default medium)
triggers:
per_task: true # reflect after each task
on_failure: true # reflect on failure
idle_batch: # idle-batch reflection
enabled: false
threshold_events: 20 # fire after this many events
threshold_hours: 12 # or this many hours since last time
The three reflection modes can coexist: per_task, on_failure, and idle_batch. Reflection re-reads conversations and updates the user profile (explicit preferences / inferred traits / behavioural patterns), and the next session opens with that context.
user / style — user & appearance
user:
display_name: "" # user display name
birthday: null # MM-DD; the characters mark the day
birthday_letter_shown: null # last year the letter was written
style:
theme: system # theme: system / light / dark
font_scale: 1.0
density: comfortable # density: comfortable / compact
motion: subtle # motion intensity
locale: en # UI language (en / zh-Hans / zh-Hant / ja / de / fr)
per_page_overrides: {} # per-page style overrides
birthday is also asked for by the onboarding wizard. Setting it is what makes the characters mark the day.
time — time awareness
time:
enabled: true # inject the real date and the away-time into prompts
gap_floor_minutes: 120 # below this, no "you were away for…" line is added
With this on, prompts carry today's date, how long you were away, and the age of recalled memories, so a character stops treating last month as today.
lan — the LAN room listener
lan:
enabled: false # master switch: off means no socket is opened at all
bind_host: "0.0.0.0" # 0.0.0.0 follows the machine onto whatever network it joins
port: 8766
cert_dir: "" # empty = ~/.fsar/security/lan
This is the master switch for outside room members; a room must also set its own lan_enabled. 0.0.0.0 is the point — it follows your machine onto whatever network it joins — but it also means a café wifi can reach the port, so leave it off there. Each outside member gets its own credential (expiry, first-use address binding, rate budgets); see SECURITY.md.
plugins / external_skills — extensions
plugins: [] # plugins
external_skills: [] # external skills (Python skills in external directories)
Further reading
- Full security model and vulnerability reporting:
SECURITY.md - Contributing:
CONTRIBUTING.md - Third-party licenses:
THIRD_PARTY_LICENSES/