Architecture
Big picture
Falco is one binary, falco. On each node it reads kernel events, evaluates them against rules, and sends matches to its outputs. The work splits across a few layers: an application layer that owns the CLI, config, outputs, and the event loop (userspace/falco/); a rule engine that loads rules and evaluates filters (userspace/engine/); and an external dependency, falcosecurity-libs (libsinsp), that captures syscalls, abstracts events as sinsp_evt, and compiles and runs filter expressions. The libs version is pinned in cmake/modules/falcosecurity-libs.cmake:45.
Components
Application layer (userspace/falco/)
Owns startup, configuration, outputs, the webserver, metrics, and the event loop. The entry point is userspace/falco/falco.cpp:59 (main). Startup is modeled as an ordered list of actions. userspace/falco/app/app.cpp:56 declares run_steps, which runs load_config, load_plugins, init_inspectors, init_falco_engine, load_rules_files, init_outputs, start_webserver, and process_events in dependency order. Each action returns a run_result that is merged at app.cpp:97. A separate teardown_steps list at app.cpp:87 always runs so cleanup is not skipped on failure.
Rule engine (userspace/engine/)
Loads rule YAML, compiles conditions into filters, and evaluates events. The public surface is the falco_engine class. Event evaluation enters at userspace/engine/falco_engine.cpp:364 (process_event).
libsinsp (external dependency)
Captures syscalls, presents each as a sinsp_evt, and provides the filter AST and the sinsp_filter execution engine that Falco's compiled rule conditions run on. Falco drives it as a sinsp inspector.
Rules (rules/)
Default rule YAML, pulled in through the falcosecurity-rules submodule.
How a request flows
One event in, one alert out:
- The event loop at
userspace/falco/app/actions/process_events.cpp:163runswhile(1)and callsinspector->next(&ev)at:164to pull one event. It branches onSCAP_TIMEOUT(:198) andSCAP_EOF(:230), and handles signals here. - The source index is resolved; in live capture it reads
ev->get_source_idx()at:244. Drop accounting runs throughsdropmgr.process_eventat:298. - The event goes to the engine at
process_events.cpp:307:s.engine->process_event(source_engine_idx, ev, s.config->m_rule_matching). No filtering happens on the inspector side, so every event reaches the engine. falco_engine::process_event(falco_engine.cpp:364) resolves the source withfind_source(source_idx)at:375and returns early ifshould_drop_evt()is true at:377. It then branches on the matching strategy at:381:ALLcollects every match intosource->m_rules(:386),FIRSTkeeps a single match (:394).- The actual evaluation is
source->ruleset->run(...). The ruleset narrows work by event type before running any filter (see Internals). - For each matched rule the engine builds a
rule_resultatfalco_engine.cpp:402, copying the event, rule name, source, output format, priority, tags, and extra fields, and returns the vector. - Back in the loop,
process_events.cpp:313callss.outputs->handle_event(...)for each result, fanning out to every configured output. Capture (PCAP-style dump) also runs here depending onm_capture_mode(:310,:322).
Key design decisions
Detection over enforcement: Falco produces alerts and does not block. This keeps the hot path cheap and leaves response to downstream tools.
Event-type indexing in the ruleset (userspace/engine/indexable_ruleset.h) avoids scanning all rules per event. Syscalls arrive hundreds of thousands per second, so the engine pre-computes which event types each rule cares about and only evaluates the relevant bucket. This is detailed in Internals.
Hot restart without killing the process: main loops falco_run while a restart flag is set (falco.cpp:67). On SIGHUP the application layer is rebuilt so rules and config are reloaded without restarting the process.
Extension points
The plugin framework adds event sources beyond syscalls (Kubernetes audit, CloudTrail, GitHub, Okta) as shared libraries (source 6, 7). Outputs are pluggable channels (file, http, program, stdout, syslog, grpc). Rules and lists are user-authored YAML loaded at startup and on reload.