Skip to content

Security and privacy

FreeDR is a single-host, single-user EDR. The service runs as LocalSystem, stores all evidence locally and serves its console only on the loopback interface. This page describes how access is controlled, how FreeDR protects itself, and what data can leave the host.

Local-only API

Item Value
Bind address 127.0.0.1 only. Any other bind value in the [api] section of freedr.toml is rejected at startup (API may bind only to 127.0.0.1).
Default port 29173 ([api] port)
Console URL http://127.0.0.1:29173/ (plain HTTP on loopback; no TLS)
Host check Every request must carry Host: 127.0.0.1:<port> exactly; localhost is rejected with 400 invalid request.
Origin check If an Origin header is present it must be http://127.0.0.1:<port>.

Every response carries Content-Security-Policy: default-src 'none'; script-src 'self'; ...; frame-ancestors 'none', X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer and Cache-Control: no-store.

Authentication

FreeDR uses one installation password and no accounts or roles (ADR-0062).

ADR-0031 is superseded

The earlier sessionless console (ADR-0031) and the token-file model (ADR-0002) no longer apply. Every API route except the login page and its static assets requires a session.

The password

  • Set during installation (setup wizard, or freedr-agent install prompts Set FreeDR password / Confirm FreeDR password). Unattended setup reads it from standard input.
  • At least 12 characters, at most 1,024 UTF-8 bytes. Never accepted on a command line.
  • Stored only as a salted Argon2id verifier (19 MiB memory, 2 iterations, 1 lane) in C:\ProgramData\FreeDR\password.json, inside the protected machine directory.
  • Reinstalling over existing data requires the existing password; installation never replaces it.
  • There is no default password and no HTTP setup endpoint. If the password file is missing or invalid, the API fails closed.

Changing and resetting the password

  • Change: Settings → Password & access → Change password (PUT /api/v1/auth/password with current_password, new_password, csrf_token). A wrong current password returns 403 incorrect current password. A successful change signs out every session.
  • Forgotten password: there is no reset command, recovery key or unauthenticated reset route. Setup asks for the existing password whenever C:\ProgramData\FreeDR\password.json exists, so recovery means an administrator resets the installation (remove the FreeDR service and delete the FreeDR machine directory from an elevated prompt, which also removes its evidence and cases) and installs again (ADR-0062). Setup's own uninstall needs the password.
  • freedrctl cannot set or reset the password.

Sessions and cookies

Property Value
Login POST /api/v1/auth/login {schema_version: 1, password} → token, expires_in, csrf_token
Session token Random 256-bit, kept only in service memory
Browser cookie freedr_session, HttpOnly; SameSite=Strict; Path=/; Max-Age=43200 (no Secure flag: loopback HTTP)
CLI Same token as Authorization: Bearer ...
Lifetime 12 hours, enforced server-side
Concurrent sessions 16; the oldest is dropped when a 17th signs in
Ends when Sign out (POST /api/v1/auth/logout), password change (all sessions), service restart (all sessions), expiry
Rate limit 5 failed attempts per minute → 429 with Retry-After: 60; one password check runs at a time
Request size Login, logout and password bodies ≤ 4 KiB
Session check GET /api/v1/auth/session

Health endpoints (/live, /ready, /version, /status) also require a session; without one they return 401 and no details. Collection and detection keep running while nobody is signed in.

CSRF protection

  • Every state-changing request (settings, cases, rules, saved queries, response, isolation, sign-out, password change) must include csrf_token in its JSON body. The token is generated randomly at service start and returned by login, GET /api/v1/auth/session and authenticated reads. A wrong token returns 403 invalid operation token.
  • Combined with SameSite=Strict cookies and the exact Host/Origin checks, this blocks cross-site requests from other web pages.
  • JSON endpoints require Content-Type: application/json and reject unknown fields.

What the session protects against, and what it does not

A valid session is the whole authority for every action, including response actions and host isolation. There is no second factor, per-action password or UAC prompt. Anyone who has the password, an unlocked signed-in browser, or a stolen session cookie can act. Administrators and SYSTEM, and code running inside the service, are outside this boundary.

freedrctl

freedrctl is a small command-line client. It reads no configuration or token files; it prompts for the password (hidden) or reads one line with --password-stdin.

Command What it does
freedrctl status GET /status
freedrctl version GET /version
freedrctl alerts GET /api/v1/detections/alerts
freedrctl rules GET /api/v1/detections/rules
freedrctl detection-health GET /api/v1/detections/health
freedrctl service start \| stop \| status \| uninstall Windows service control; verifies the password locally against password.json (works while the API is down) and still needs Windows administrator rights
freedrctl service install Refused: install by running freedr-agent (approve the UAC prompt) or the setup wizard

Global options: --port <1-65535> (default 29173), --password-stdin. There is no --host or --password option. API commands log in to 127.0.0.1 only, never follow redirects or proxies, time out after 10 seconds and sign out when done.

Protected settings

Provider settings and credentials (Settings → Targeted enrichment and AI) are served by GET/POST /api/v1/settings/enrichment (ADR-0058, simplified by ADR-0060).

  • Saving applies immediately with a session and CSRF token. The UAC/approval-helper flow described in the original ADR-0058 is not present.
  • Each save carries expected_revision; a stale revision returns 409 settings revision changed.
  • Credentials are write-only. Each slot (abuse_ch, abuse_ipdb, virus_total, alien_vault_otx, grey_noise, cheaper_inference, open_ai, anthropic, deep_seek) can be kept, replaced or removed. The API only reports which slots are configured; keys are never returned, logged or stored in the browser.
  • Keys are encrypted with Windows DPAPI bound to the service account (LocalSystem) and stored in the protected control database.
  • Every outbound provider starts disabled; enabling a keyed provider without its key is rejected.

Self-protection (tamper resistance)

Partial: user-mode only

FreeDR implements unsigned, user-mode tamper resistance (ADR-0063). It resists ordinary (non-administrator) local users and processes and accidental changes. It does not stop administrators, SYSTEM, kernel code or offline disk access. Driver, PPL, ELAM and code-signing protection are not available.

What is implemented:

  • Process DACL: at startup the service process is locked so that only SYSTEM has full access; everyone else can only query limited information and wait. Ordinary callers cannot terminate, suspend, inject into or re-permission it.
  • Service DACL: SYSTEM and elevated Administrators keep full control; everyone else may only query. Ordinary users cannot stop, delete or reconfigure the FreeDR service.
  • Crash recovery: the service restarts after 5, 30 and 120 seconds, then stops retrying until a day passes without failure. Failures reported at startup (non-zero exit) are recovered like crashes. An intentional stop always stays stopped.
  • Protected directories: C:\ProgramData\FreeDR and its data, data\cold, config and run directories are SYSTEM-owned with full access only for SYSTEM and Administrators, and an OWNER RIGHTS entry so the installing user cannot silently re-permission files they created. Reparse points are rejected.
  • Drift repair: if the machine directory's DACL has gained extra allow entries (for example after Explorer's "Continue" prompt), the service restores the canonical DACL, re-verifies trusted files (freedr.toml, password.json, ai-credential.dpapi, policy files, databases, Sysmon config) and raises the Medium self alert freedr.self.machine-state-access-restored. A wrong owner, deny entries, a reparse point or a tampered trusted file stops startup instead.
  • Stop alert: after a requested stop, the next start raises freedr.self.service-stopped with the stop time.
  • Startup failures are written to the service log and the Windows Application event log (source FreeDR).
  • Built-in rules are embedded in the executable; custom rules, toggles and exclusions live in the protected control database.

Qualification: one Windows 11 (build 22621) VM campaign passed on 2026-10-03 (docs/QA-REPORT-TAMPER-RESISTANCE-2026-10-03.md). Windows 10 and other builds are not qualified.

Threat model highlights

From docs/architecture/THREAT_MODEL.md and the ADRs above:

  • Untrusted inputs: telemetry, rule content before validation, API input before authentication, AI output, cold archive files. Collectors cannot open storage; only the database worker does.
  • Only freedr-response-win changes Windows state, and only through registered declarative response actions (see Response).
  • Hunt and detection SQL are read-only and cannot load extensions or reach files or the network (see Search and SQL Hunt).
  • AI cannot run shell commands, write evidence, change settings or exclusions, or isolate the host. It can run a limited set of containment actions only when you turn on Let AI contain threats it finds (off by default; see Response).

Known residual risks (not mitigated today):

Risk Status
Administrator/SYSTEM compromise Out of scope; can subvert all local controls.
Rule packs and updates are unsigned Built-in rules are reported as unsigned; update authenticity is not enforced.
Response verdict integrity Relies on the trusted local repository (KP-002).
Evidence at rest Protected by ACLs, not encrypted by FreeDR.
Session theft A stolen cookie or unlocked browser grants full authority until expiry or sign-out.
Telemetry loss under overload Possible; reported in Health.

Data privacy

Stored locally

All under C:\ProgramData\FreeDR (SYSTEM/Administrators only):

Data Location
Recent evidence data\evidence.duckdb
Archived evidence data\cold\ (Parquet)
Cases, alerts, rules, saved queries, response jobs, AI runs, settings, encrypted keys data\control.sqlite3
Password verifier password.json
Internal AI harness credential (DPAPI) ai-credential.dpapi
Configuration and policy freedr.toml, config\
Quarantine vault Encrypted objects under the machine directory

Evidence is kept for retention_days (default 90, editable in Settings → Evidence lifecycle). FreeDR has no telemetry upload, cloud account or fleet server.

What can leave the host

Nothing is sent by default except the Sysmon download below. Everything else is opt-in.

Feature Default Destination What is sent
Sysmon bootstrap At install, and from Settings → Sources, only if Sysmon is not installed https://download.sysinternals.com/files/Sysmon.zip A download request
Threat-intel enrichment All providers off CIRCL hashlookup.circl.lu (no key); keyed: MalwareBazaar, ThreatFox, URLhaus (abuse.ch), AbuseIPDB, VirusTotal, AlienVault OTX, GreyNoise Community Only indicators the provider supports: SHA-256 hashes; public IPs; public domains; exact URLs (URLhaus). No file samples, command lines, private/reserved IPs, internal domains or credentials.
AI investigator and assists Off One selected provider: CheaperInference, OpenAI, Anthropic or DeepSeek (fixed HTTPS endpoints) Bounded case evidence and tool results, redacted (see below)
Web research (AI or console) Off (web_enabled) Parallel search https://search.parallel.ai/mcp, then public web pages it chooses to read Short sanitized search queries; page fetch requests

Enrichment details (ADR-0058, ADR-0073):

  • Fixed HTTPS endpoints, redirects disabled, bounded responses.
  • Per-provider daily limits (default 100; VirusTotal at most 500/day and one request per 15 seconds). Results are cached as observations on the case.
  • Automatic lookups run for alerts that open or join an open case and for events attached to such a case; they can also be requested manually or by the AI.
  • Offline tags (LOLBAS, remote-access tools) and host prevalence/"new to host" facts are computed locally and send nothing.

AI details (ADR-0061, ADR-0064):

  • Disabled until you enable it and enter that provider's key. Entering an AI key in the setup wizard enables AI unless you turn it off. Let AI contain threats it finds stays off unless you turn it on.
  • When enabled, automatic investigation passes start for open cases of Medium severity or higher; you can also start runs manually. A daily token limit applies.
  • Before anything is sent, FreeDR redacts: values of fields named like password, secret, api_key, authorization, access_token, refresh_token, host_id, username; user and machine-specific Windows SIDs (well-known SIDs such as SYSTEM, built-in groups and RID 500 are kept, see AI overview); text containing credential markers (password=, token=, bearer, --password, api_key=, ...); and the user name in C:\Users\<name>\ paths. Other evidence (command lines, file paths, hashes, domains, IPs) is sent as needed for the investigation.
  • Provider routes are not described as zero-data-retention; the provider's own data policy applies.

Web research details: at most three searches and three page reads per AI pass, 10-second timeout, 256 KiB of extracted text per page. Queries are limited to 200 characters and rejected if they contain private IPs, internal-looking domains (.local, .corp, .lan, ...), localhost, or anything resembling a credential or long opaque token. Page reads go only to public addresses (every DNS answer is checked) and are parsed as inert text.

Windows alert notifications (ADR-0067) are local toast notifications and send nothing off the host.