The short version

MARC connects your iPhone to your Mac over SSH. Your Mac collects the readings, runs commands, and handles file actions. MARC sends requests through the encrypted connection and turns the replies into screens and graphs.

  1. You connect to your Mac’s Remote Login service using a Mac account.
  2. MARC checks the Mac’s SSH host identity, then authenticates your account.
  3. A monitoring collector starts automatically. Other readers run when their views need them; changes such as uploading a file or stopping a process require an action from you.
  4. Readings return to iPhone. Monitoring history is also saved under your Mac account; previews and downloads create temporary iPhone copies.

Your Mac must be awake and reachable from your iPhone. MARC connects directly, with no built-in internet relay. For access away from home, you need a secure route such as a VPN you configure separately. Start with Connect your Mac.

Identity, login, and data flow

Before signing in to a new Mac, MARC reads its public SSH host key and asks you to compare the key type and SHA256 fingerprint with that Mac. It remembers your approval for the host and port. An unexpected key change blocks later connections. Approval alone does not prove you reached the intended Mac.

Password login sends your username and password inside SSH. Key login uses an imported OpenSSH Ed25519 private key to sign on iPhone, without uploading the private key or passphrase. Your Mac must already authorize the matching public key. See SSH keys and Security & privacy.

After login, MARC checks that the server runs macOS. It sends embedded scripts to python3 through SSH. Monitoring and process collectors return one JSON object per line; other helpers return a JSON result. MARC reads these replies to update the display. Terminal output and SFTP transfers use separate formats.

Which features share a connection?

One SSH connection can carry several channels. A new command channel does not necessarily open another network connection.

FeatureConnection and channel
MonitoringThe main authenticated SSH connection runs a long-lived, non-interactive command channel for the collector.
Processes and Status top processesSeparate streaming command channels on that main connection, opened by the views that need process readings. These are not interactive Terminal shells.
History, termination, Control Center, Services & DockerShort command channels on the main connection. Controls and Services readers can refresh automatically while their views are active.
FilesA separate authenticated SSH connection opens an SFTP subsystem channel. Directory listings, metadata, text editing, and folder archives also use Python command channels on that Files connection. SFTP carries file bytes and performs operations such as canonical-path lookup and upload rename.
TerminalEach session opens a separate authenticated SSH connection and requests an interactive shell with an xterm-256color pseudo-terminal (PTY). The current limit is four sessions. Local AI generation opens a command channel on that Terminal connection.
Test SSH and host probeThe host-key probe is a separate unauthenticated key exchange. Test SSH opens and closes its own authenticated connection for fixed read-only checks.

What runs on your Mac

Monitoring starts automatically. A Python collector reads CPU, memory, storage, network, and available power information. It also tries to save history, even when Statistics is closed.

Process readers run when needed. Status’s top-process preview and the Processes view read a stream based on ps. Each stream uses a temporary stop marker. The reader can appear in its own process list.

Helpers handle individual requests. Browsing files, reading history, and refreshing controls or services run short requests. Uploading, editing, deleting, stopping processes, or changing controls requires your action.

Terminal opens a Mac shell. Your shell’s startup files and the programs you run determine what else starts. Files uses the Mac’s SFTP subsystem.

Python 3 must be installed and available as python3 to the SSH account. MARC supplies its scripts with each request; its collectors need no extra Python packages. Connecting does not install Python, a persistent MARC helper, a LaunchAgent, a LaunchDaemon, a scheduled job, or an executable collector script.

MARC does write history, stop markers, and temporary transfer files. Commands you run, optional tool installation, and service actions can make lasting changes. See Control Center and Services & Docker for prerequisites.

How to read the numbers

CPU: total and per-core activity come from changes in macOS CPU ticks, excluding idle ticks. User CPU includes user and nice ticks; system CPU is separate. These are whole-Mac readings, including other apps and macOS—not a measurement of MARC’s overhead.

Processes: CPU compares accumulated process CPU time with elapsed time. One busy core is about 100%, so a process can exceed 100%. Process memory is resident size (RSS). These figures do not add up directly to the whole-Mac display.

Memory: MARC subtracts free, inactive, and speculative pages from physical RAM using vm_stat’s page size. The estimate includes active, wired, and compressed resident usage; swap is separate. When macOS reports file-backed pages, MARC uses them to estimate cached files. MARC does not report a separate compressed-memory amount or reproduce Activity Monitor’s categories exactly.

Storage: capacity comes from the APFS Data volume. Displayed used space is total minus available, including shared-volume usage; it is not a folder-size total. Disk activity comes from storage-driver byte counters and is not limited to that volume.

Network and disk activity: MARC subtracts successive byte counters and divides by elapsed time. Network readings cover the default-route interface. Missing counters, a reset, or an interface change can leave rates unavailable until a valid pair arrives. These readings are not internet speed tests.

Sampling, slower readings, and gaps

The monitoring loop sleeps one second between collections, plus the time needed to run commands. The 1, 2, or 5 second refresh setting controls display updates; it does not change that sleep or every helper’s interval.

Storage capacity, process count, swap, and local IP refresh about every five seconds. Battery charge and power source use the selected 5, 10, 30, or 60 second interval (10 by default), applied when reconnecting. Battery health is read in a separate thread at most about once a minute when a battery is detected. Processes target one-second cycles; Control Center waits five seconds between refreshes, with wireless reads roughly every 30 seconds. Services & Docker readers use an eight-second refresh cadence. Request duration and failures can lengthen all these intervals.

Missing data does not mean zero usage. Some optional values become unavailable; slower readings can retain an earlier value after failure. A failed capacity read currently returns zero placeholders, not a real zero-capacity disk. Check connection status, update feedback, and permissions.

Invalid collector output can leave the last readings visible with a delay message. Repeated errors or about 90 seconds without a valid sample end the monitoring connection and allow reconnection when eligible. This does not check the freshness of every optional field.

The iPhone’s live chart buffer keeps about five minutes, capped at 300 points. Saved-history views request 15, 30, or 60 minutes and average samples into five- or ten-second buckets. Archived rates are not calculated across gaps over 120 seconds. Disconnections, backgrounding, failed writes, and clock changes can affect the record; charts do not imply continuous recording or reconstruct missing activity.

For the screens and common interpretation questions, see Monitoring and Processes.

Data and command reference

Expand a group for the built-in commands and APIs. Commands run on your Mac unless stated otherwise. Optional tools must already be installed. Angle-bracket values such as <pid> are placeholders.

Monitoring and processes
FeatureInformation accessedCommand or APIWhenPurpose
Compatibility and connection testOS identity; test also reads account and hostnameuname -s, sw_vers -productName; Test SSH adds whoami, hostname, uname -sm, sw_vers -productVersionAfter authenticated connection; extra checks when Test SSH is selectedConfirm macOS and report login context
CPU and per-core activityUser, system, idle, nice countersPython ctypes loads /usr/lib/libSystem.B.dylib: mach_host_self, host_statistics (flavor 3), host_processor_info (flavor 2), vm_deallocateCollector startup and each live cycleCalculate activity from tick differences; release per-core allocations
Memory and swapRAM size, virtual-memory pages, swap usedsysctl -n hw.memsize, vm_stat, sysctl -n vm.swapusageRAM once; pages each cycle; swap about every five secondsEstimate RAM usage and report separate swap use
System contextHostname, username, logical cores, model, OS, architecture, boot time, uptime, one-minute load, process countPython socket.gethostname(), getpass.getuser(), os.getloadavg(); sysctl -n hw.logicalcpu, hw.model, kern.boottime; sw_vers -productVersion, uname -m, ps -axo pid=Identity mostly once; load/uptime each cycle; count about every five secondsLabel the selected Mac and explain its current state
StorageData-volume total, used and available blocksdf -k /System/Volumes/DataAbout every five secondsDisplay capacity and available space
Disk activityDriver-level cumulative bytes read and writtenioreg -r -c IOBlockStorageDriver -d 1 -a; retries without the depth limit if no counters are foundEach live cycleCalculate disk read/write rates between samples
NetworkDefault-route interface, received/sent bytes, local IPv4 address when availableroute -n get default, netstat -ibn, ipconfig getifaddr <interface>Route/counters each cycle; IP about every five secondsShow interface activity and connection context
PowerCharge, source, charging state; available condition, maximum capacity and cyclespmset -g batt; system_profiler SPPowerDataType -jsonSelected power interval; health at most about once per minuteReport battery and power details
ProcessesPID, account, resident bytes, start time, accumulated CPU time, executable basenameps -axo pid=,user=,rss=,lstart=,time=,comm=While a process stream is requestedBuild process lists and CPU deltas; this collector does not request full command-line arguments
Terminate processSelected process’s current identityps -p <pid> -o user=,lstart=,comm=, Python os.kill(pid, signal.SIGTERM)After user confirmationRecheck identity and request termination, subject to account permissions
HistoryTimestamped monitoring JSONPython sqlite3; writes marc_samples_v1, reads it and legacy samplesSave about every five seconds; read on history requestProvide recent Statistics beyond the live buffer
Files and Terminal
FeatureInformation accessedCommand or APIWhenPurpose
Browse and inspectNames, sizes, dates, types, paths, ownership and permissionsSFTP path resolution; Python os.scandir, os.lstat, pwd, grpOpening/reloading folders or item detailsDisplay directory listings and metadata. For eligible text files, the detail helper also reads up to 1 MiB on the Mac to check editor support; that check does not return the content to iPhone.
Preview, download, uploadSelected file content and transfer metadataSFTP file reads/writes, attributes and rename; Python staging and replacement checksUser-selected transferCopy content between Mac and iPhone; validate an existing destination before replacing it
Edit, rename, move, permissions, deleteSelected path, content for editing, identity and metadataPython file I/O, os.replace, macOS renamex_np, os.chmod, os.unlink, shutil.rmtree; metadata APIs include fcopyfile and quarantine extended attributesUser-selected action; text bytes staged over SFTP for savingChange real files; deletion bypasses Trash. Replacement attempts to preserve applicable metadata and quarantine.
Folder downloadSelected folder’s entries and contentsPython tempfile, tarfile, os.scandir; then SFTPUser-selected folder download/shareBuild a bounded .tar.gz archive on the Mac and transfer it
TerminalYour input and the shell/program outputSSH PTY and shell request; commands chosen by you and your shell’s startup filesOpening a session and entering commandsRun a real interactive Mac shell. There is no fixed command inventory for user-run programs.
Control Center, Services & Docker, and AI
FeatureInformation accessedCommand or APIWhenPurpose
Audio and displayVolume/mute, output device names/IDs, display IDs/resolutions and supported brightnessosascript -e with fixed volume scripts; CoreAudio AudioObjectGetPropertyData/AudioObjectSetPropertyData; CoreGraphics display APIs and DisplayServices brightness APIs through ctypesControl refresh; writes on user adjustmentRead settings and adjust supported outputs/displays
WirelessWi-Fi interface, power and network name when available; Bluetooth powernetworksetup -listallhardwareports, -getairportpower, -getairportnetwork; writes -setairportpower; optional blueutil --power [0|1], otherwise system_profiler SPBluetoothDataType -json for readingPeriodic wireless reads in Controls; writes on user actionReport state and offer supported switches; turning off Wi-Fi can cut the SSH connection
Media, lock, display sleep/wake, Quick LaunchOpen players; available Now Playing title, artist and source; installed app names/bundle IDs; session/display statepgrep -u <uid> -x Music or Spotify, fixed osascript playback scripts; optional media-control get and advertised playback verbs; app Info.plist reads; open -b <bundle-id>; pmset displaysleepnow, caffeinate -u -t 1; CoreGraphics session dictionary and SACLockScreenImmediate through PythonMedia reads during Controls refresh; catalog on request; changes on user actionShow/control existing playback, launch selected apps, and request session/display controls. Media artwork stays on the Mac in MARC’s response path.
Listening portsTCP listening endpoints and process identity/ownershiplsof -nP -a -iTCP -sTCP:LISTEN -Fpcn; ps -axo pid=,uid=,ppid=,lstart=,comm=; eligible stops use SIGTERMServices refresh; stop after confirmation and identity rechecksList listeners and stop eligible account-owned processes with protected-process checks
Homebrew servicesService names and statesbrew services list --json; brew services run <name>; brew services stop --keep --max-wait=5 <name>Services refresh; start/stop on user actionManage supported existing services. Start does not add launch-at-login registration; Stop preserves it.
DockerLocal socket selection; container ID, name, image, state/statusdocker context inspect --format '{{json .Endpoints.docker.Host}}' when needed; docker --host <unix-socket> ps -a --no-trunc --format '{{json .}}'; start <id> or stop --timeout 10 <id>Docker refresh; start/stop on user action after state and host checksManage eligible existing local containers; remote TCP Docker hosts are rejected
Local AIInstalled model names; your prompt, selected model and command-generation instructionsPython urllib.request over SSH to http://127.0.0.1:11434/api/tags or /api/chat on the MacModel refresh or user-requested generationAsk configured Ollama for a Terminal command suggestion; no automatic execution
API AISubmitted prompt, model, instructions, selected API key and ordinary request metadataiPhone HTTPS via URLSession to OpenAI, Google Gemini, Groq, or a custom endpoint; separate model-catalog requestGeneration after destination consent; catalog loading after separate approvalSuggest a Terminal command. No built-in Mac command or AI proxy is involved in API requests.

Controls use supported capabilities where available. Some paths call private macOS frameworks and can change or fail between macOS versions. MARC does not install optional blueutil, media-control, Homebrew, Docker, or Ollama automatically. Review setup commands before running them.

What Activity Monitor may show

Activity Monitor may show SSH sessions, Python collectors, short-lived commands such as ps, vm_stat and ioreg, an SFTP server, and your Terminal programs. Names vary with macOS and your installation: examples include sshd, sshd-session, Python, python3, sftp-server, and zsh.

MARC does not hide its collectors from the process list. A reader or its subprocess can appear there too. Check its account, identity, and timing; a process name alone does not identify it as MARC’s.

Storage, retention, and cleanup

The collector tries to save monitoring JSON about every five seconds in ~/.local/share/marc/history.sqlite. The marc_samples_v1 table stores a timestamp and payload under the SSH account. New history files and directories use private permissions; existing directory permissions are not automatically repaired.

Samples contain CPU and per-core activity, memory, storage, network/disk counters, interface and time. They also include hostname, username, local IP, hardware/OS details, core/process counts, uptime, load, swap, and available power/health readings. They do not include process lists, Terminal transcripts, file contents, control/service results, or AI prompts.

On its first successful save, then about once a minute while active, the collector removes its own rows older than two hours. Deletion is not scheduled after disconnect. Legacy samples rows remain readable. Failed writes retry later without stopping valid live readings.

Removing a profile or uninstalling MARC does not delete history on the Mac. Stop MARC sessions using that account before removing the database separately. Deletion does not securely erase disk pages, backups, or administrator logs.

Temporary files and iPhone storage
  • Process streams: ~/.local/share/marc/process-stream-<UUID> is an empty stop marker, not an executable. The loop checks its existence. On cancellation MARC attempts to unlink it using another SSH command; the reader also removes it on normal exit. A broken connection or abrupt termination can leave a marker. Removing an abandoned marker stops a reader only if it is still running and checking that marker.
  • Uploads and edits: Mac destination folders can contain .marc-upload-<UUID>.part and .marc-edit-input-<UUID>.part staging files. Text replacement can also create a .marc-edit- temporary file. Helpers attempt private staging permissions and cleanup. An interrupted operation can leave content behind; check its outcome before deleting leftovers or retrying.
  • Folder archives: Python creates a marc-folder- directory under the Mac account’s temporary directory, containing a .tar.gz. MARC attempts cleanup after transfer or preparation failure. If the app cannot receive the archive path or contact the Mac again, removal may not complete. No MARC scheduled sweeper is installed on the Mac.
  • iPhone copies: previews, downloads and app-imported uploads use MARC-owned temporary storage with iOS Complete Protection and backup exclusion. Cleanup is attempted when the last transfer/preview/share owner releases its copy, and at startup for recognized abandoned folders. Failures or system interruption can leave copies until later cleanup or system removal. Exported Files or other-app copies are independent; MARC does not delete them.
  • Other local state: profiles/preferences use app storage; saved passwords, SSH keys/passphrases, API keys and trusted host identities use device-only, unlocked-device Keychain access. Current readings, process/service results, and Terminal display buffers are held in app memory. Shells and services can keep their own logs/history on the Mac. Profile removal is separate from removing reusable keys and fingerprints; uninstalling does not reliably clear Keychain.

See Files, Security & privacy, and the Privacy Policy for storage choices and limits. Only remove files you have identified as abandoned and no longer need.

Permissions and connection lifecycle

Every operation uses the connected Mac account’s permissions. macOS privacy protections, desktop-session requirements, and installed tools’ access still apply. SSH does not bypass these restrictions or create a separate MARC sandbox.

Monitoring and list queries read data, but monitoring also writes history and process readers create stop markers. File changes, termination, controls, app launches, and service actions change the Mac. Stopping a process can lose unsaved work, and a manager may restart it. Disconnecting does not undo completed actions.

When MARC enters the background, it cancels monitoring, pauses Terminal sessions, stops Files work, and attempts to close their connections. Readers tied to a view are canceled when no longer needed. Foreground reconnection starts new work; it does not fill recording gaps. Terminal text can remain in memory after the remote session ends.

Cancellation and remote process exit do not happen at the same instant. The monitoring collector exits when its output raises a broken-pipe error; process readers also use stop markers. Network loss, a blocked command, or delayed SSH teardown can delay cleanup.

Your programs may stop or continue after disconnection, depending on their configuration. MARC does not guarantee persistent Terminal jobs or automatically stop services and containers you started. Check the outcome before retrying an interrupted action.

What can leave your devices?

Monitoring, Files, Terminal and control replies travel directly over SSH between your devices, with no MARC-operated relay. Discovery and hostname lookup also use local or network naming services.

AI starts Off and is available only for Terminal command suggestions. Local mode sends your prompt, model and instructions over SSH to Ollama on the Mac’s loopback address. Its configuration determines further logs and network activity. API mode sends them from iPhone to your chosen OpenAI, Google Gemini, Groq, or custom HTTPS endpoint, including the selected API key and normal request metadata such as your public IP.

The first API prompt to an endpoint in a Terminal session needs approval. Later prompts still require your action; connection or relevant settings changes reset approval. Loading a provider’s model list asks separately and sends no prompt. Terminal output, files and Mac credentials are not attached automatically, but anything pasted into your prompt is sent. Review suggestions at an empty shell prompt before choosing Run.

Commands you run, tool installation, launched apps, and services or containers can make their own network connections. File providers may fetch cloud content; sharing destinations receive the copies you send. See Local & API AI and the Privacy Policy for recipients, consent and retention.

Why use this approach?

SSH uses macOS Remote Login and your existing account over an encrypted connection. macOS tools provide the readings without a separately installed MARC monitoring agent. The source is open for inspection.

Python must be available, your Mac must stay reachable, and permissions and hardware support vary. Collectors, commands, history writes, encryption and transfers use resources. AI models and programs you start add their own load. MARC does not claim zero overhead or a measured performance figure.

For connection or collection problems, see Troubleshooting.