Documentation · Config & development
Documentation menu
Module reference
Modules
Safety & execution
securitysandboxskills
Interface & extensions
frontendskinsocial

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.yaml is 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.tier and reflection.intensity below 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.judge set, 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: responses to 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_model is only used when set; otherwise image analysis goes through the active chat model.
  • judge must speak JEV's protocol — it returns typed judgments rather than text, so an ordinary OpenAI-compatible model cannot be dropped in. base_url and api_key accept ${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

Source docs-public/configuration.mdView on GitHub ↗