ExecutionUser activityPersistence
Snap and Flatpak Artifacts: Linux Sandboxed App Forensics
Snap and Flatpak evidence on Linux: snapd state.json change history, sideloaded snaps, Flatpak installations and remotes, and per-app data directories.
- Location
- /var/lib/snapd/, /snap/, ~/snap/; /var/lib/flatpak/, ~/.local/share/flatpak/, ~/.var/app/
- Proves
- Which sandboxed applications were installed, refreshed or removed, from which store or remote, whether any were sideloaded, and where each app kept its user data
- Timestamps
- RFC 3339 times with zone in snapd state.json; journal UTC entries for Flatpak history; directory and deployment file times
- Access
- root for /var/lib/snapd/state.json; world-readable Flatpak system installation; user data owned by each user
- Retention
- Installed apps and data persist until removal; snapd prunes old change records; Flatpak history lasts as long as the journal
- Collection
- UAC, Velociraptor, cp -a, tar
Tools
Compare all tools- snapCLI · built into Linux
- flatpakCLI · built into Linux
- jqCLI · open source
- journalctlCLI · built into Linux
- findCLI · built into Linux
What it is
Snap (Canonical, default on Ubuntu) and Flatpak (default on Fedora Workstation and widely available on Debian, SUSE and Arch) install applications outside the distribution's package manager. Their installs, updates and removals do not appear in dpkg or RPM logs, and each app keeps its user data in its own directory, not in the usual ~/.config and ~/.mozilla paths.
Missing these locations is a common reason for "no evidence found": on Ubuntu desktops, Firefox, Chromium, Thunderbird and many chat clients are snaps by default.
Where it lives
| Item | Snap | Flatpak |
|---|---|---|
| System state | /var/lib/snapd/state.json (JSON, root only) | /var/lib/flatpak/ (OSTree repo, app/, runtime/, exports/) |
| Package files | /var/lib/snapd/snaps/<name>_<rev>.snap (SquashFS) | /var/lib/flatpak/app/<id>/<arch>/<branch>/<commit>/ |
| Mount points | /snap/<name>/<rev>/, /snap/<name>/current | none; deployed trees |
| Per-user install | not supported | ~/.local/share/flatpak/ |
| App system data | /var/snap/<name>/<rev>/, /var/snap/<name>/common/ | none |
| App user data | ~/snap/<name>/<rev>/, ~/snap/<name>/common/ | ~/.var/app/<app-id>/{config,data,cache}/ |
| Sources | Snap Store (snapcraft.io), or local file | Remotes: /var/lib/flatpak/repo/config, /etc/flatpak/remotes.d/, ~/.local/share/flatpak/repo/config |
| Permissions | snap connections; AppArmor profiles in /var/lib/snapd/apparmor/profiles/ | Overrides: /var/lib/flatpak/overrides/, ~/.local/share/flatpak/overrides/ |
| Services | systemd units snap.<name>.<app>.service in /etc/systemd/system/ | none (apps are user-launched) |
| Logs | journal (snapd unit) | journal, read by flatpak history |
What it proves
- Snap installs, refreshes, reverts and removals with start and end times (
changesinstate.json,snap changeslive). - Sideloading: snaps installed from a local file get revisions like
x1,x2, and--dangerousor--devmodeinstalls skip store signatures and confinement. - Flatpak installs, updates and uninstalls, with the remote, ref and commit, from
flatpak historyjournal entries. - Which remotes a host trusted. An unfamiliar Flatpak remote or a snap from an unexpected publisher is a supply-chain lead.
- Where the app kept user data: browser profiles, chat databases and downloads caches under
~/snap/and~/.var/app/. - It does not record when a user ran an app; use the journal, thumbnails and the app's own data.
Key fields
jq '.changes[] | {id, kind, summary, status, "spawn-time", "ready-time"}' state.json | head -40
jq '.data.snaps | to_entries[] | {name: .key, current: .value.current, type: .value.type,
devmode: .value.devmode, sequence: [.value.sequence[] | .revision]}' state.json
| Field | Meaning |
|---|---|
changes[].kind | install-snap, refresh-snap, remove-snap, revert-snap, auto-refresh |
spawn-time, ready-time | Start and completion of the change, RFC 3339 with zone |
data.snaps.<name>.sequence | Installed revisions; x<N> means local, unasserted |
devmode, jailmode, classic | Confinement flags; classic snaps have full system access |
| Flatpak history columns | Time, change (deploy install, deploy update, uninstall), application ref, branch, installation, remote, commit |
Timestamps
snapd writes RFC 3339 times with a zone offset or Z in state.json. Flatpak history comes from structured journal entries, stored in UTC microseconds. Directories under /var/lib/snapd/snaps/, /var/lib/flatpak/app/ and ~/.var/app/<id>/ carry creation times close to the first install or first launch; the per-user data directory is created at first launch, which is useful when the history is gone.
Retention
snapd prunes completed changes after a while, so state.json keeps recent history only (hours to days on busy systems); older activity survives in the journal. snapd keeps a small number of revisions of each snap (refresh.retain, 2 by default on current releases), so older .snap files and ~/snap/<name>/<rev>/ directories may still be there. Flatpak keeps no history file of its own; flatpak history is empty once journal retention has passed. Uninstalling a Flatpak leaves ~/.var/app/<id>/ in place unless the user passes --delete-data.
Collection
tar -C /mnt/evidence -cpf /cases/2026-017/sandboxed.tar var/lib/snapd/state.json var/lib/snapd/apparmor \
var/snap etc/systemd/system/snap.* var/lib/flatpak/repo/config var/lib/flatpak/overrides \
etc/flatpak home/*/snap home/*/.var/app home/*/.local/share/flatpak/repo/config 2>/dev/null
ls -la --time-style=full-iso /mnt/evidence/var/lib/snapd/snaps/ /mnt/evidence/var/lib/flatpak/app/ > inventory.txt
# Live
snap list --all; snap changes; flatpak list --columns=all; flatpak history; flatpak remotes -d
UAC runs snap list and flatpak list in live response and collects the journal. The .snap and OSTree payloads are large; hash them and collect selectively.
Parsing
S=/cases/2026-017/var/lib/snapd/state.json
jq -r '.changes[] | [."spawn-time", .kind, .status, .summary] | @tsv' "$S" | sort
jq -r '.data.snaps | to_entries[] | select(any(.value.sequence[]; (.revision|tostring)|startswith("x"))) | .key' "$S"
# Flatpak history offline, from the journal
journalctl -D /cases/2026-017/var/log/journal -t flatpak -t flatpak-system-helper -o short-iso-precise
journalctl -D /cases/2026-017/var/log/journal -u snapd -o short-iso-precise | grep -iE 'install|remove|refresh'
On a live system, flatpak history --columns=time,change,application,remote,commit formats the journal entries directly.
Investigator tips
- Browser, messaging and mail evidence in snaps and Flatpaks lives under
~/snap/<name>/common/or~/.var/app/<id>/; point your browser profile parsers there. - Snap services run as root through systemd units named
snap.<name>.<app>.service. A service from a localx1revision is persistence that bypassed the store. - Classic confinement and
--devmoderemove the sandbox. Treat such snaps like any unpackaged binary. - An extra Flatpak remote added with
flatpak remote-add(visible inrepo/config) can deliver malicious updates to already-installed apps; check its URL and GPG key. - Removal of a snap leaves an automatic snapshot of its data in
/var/lib/snapd/snapshots/for about 31 days on recent snapd versions, which can hold data the user thought was gone.