Skip to content
FreeBSDDeep Dive Published Updated 7 min readViews unavailable

FreeBSD script(1): Record Terminal Sessions for Repeatable Operations

Record and replay FreeBSD terminal sessions with script(1), understand what the transcript captures, and protect logs from secrets and unsafe reuse.

FreeBSD’s script(1) records a terminal session to a typescript file. It is useful for capturing a reproducible maintenance transcript, demonstrating a procedure, or collecting output that would otherwise disappear from a remote terminal. It is not a full audit system, shell-history database, or tamper-proof recording service. Its mode determines which streams and timing information it saves, and its output may contain credentials or terminal escape sequences.

Choose the recording mode before starting. The default mode captures terminal output. -r records input, output, and timing information for later playback. -k records keystrokes as well as output, which can be particularly sensitive. The transcript can include passwords typed into an interactive program if the terminal echoes them, copied tokens, database queries, private hostnames, and command output. Restrict access before the session begins, not after a sensitive string has already been written.

Create a new, private transcript

Create a unique file in a directory with an appropriate owner and restrictive permissions. Avoid a predictable shared path and avoid append mode for incident evidence:

umask 077
mkdir -p "$HOME/session-records"
chmod 700 "$HOME/session-records"
script "$HOME/session-records/change-20261003.typescript"

The command starts a subshell and records terminal dialogue until the child shell exits. The selected shell depends on the environment and documented defaults. A typescript file may include start and completion banners as well as prompts. After the session, verify ownership, file permissions, size, and that the recording ended cleanly:

ls -l "$HOME/session-records/change-20261003.typescript"
tail -20 "$HOME/session-records/change-20261003.typescript"

Do not blindly trust the transcript’s displayed commands as a replayable script. It is presentation output, not shell syntax. Terminal control sequences, wrapped lines, prompts, and interactive programs can make a transcript unsuitable for copy/paste or machine execution.

When an automation should run one command rather than an interactive shell, provide both the output file and command as the manual requires:

script /var/tmp/health-check.typescript /bin/sh -c 'service sshd status'

This example records a simple command’s terminal output. Review shell quoting for your exact command; script receives an argument vector and does not automatically interpret arbitrary shell syntax unless a shell is explicitly invoked. Do not use this pattern to wrap destructive maintenance and then treat the resulting transcript as proof that the action was approved or successful.

Capture timing and input only when justified

Use -r when a human review genuinely needs input, output, and timestamped session playback:

script -r "$HOME/session-records/diagnostic-session.typescript"

Replay the recorded session with the FreeBSD script playback mode:

script -p "$HOME/session-records/diagnostic-session.typescript"
script -T '%Y-%m-%d %H:%M:%S ' "$HOME/session-records/diagnostic-session.typescript"

The exact -T format and playback behavior are release-specific; consult the installed script(1) manual. Playback displays recorded content. It does not rerun the original commands, and it does not validate their effects. A recording that includes interactive input can expose private data during review, so treat both the typescript and any copies as sensitive operational artifacts.

-t time controls how frequently output is flushed to the transcript, with documented defaults and behavior in the local manual. A small interval can reduce loss if a terminal process or host fails, but it can increase write activity. A flush interval is not the same as a durable remote backup; a disk, host, or filesystem failure can still destroy the local log. For high-value evidence, use a secure external logging system in addition to local capture, with an explicit retention and access policy.

Use -k only in a deliberately approved environment because it logs keystrokes. It is not a secure way to capture a shell’s secrets or a substitute for an audit trail. Prefer sanitized command logs, application audit records, or a controlled test account for procedures involving passwords, private keys, recovery codes, API tokens, or personally identifiable information.

Know what a transcript can and cannot prove

A transcript can show what appeared on a terminal during the recording. It does not necessarily establish the identity of the person at the keyboard, the integrity of the host, the state of the system before recording, or whether a command’s output was altered later. A local privileged user can replace the file. The timestamps in a session recording are useful context, but they are not a trusted time source or a signed event log.

For incident evidence, record the host name, FreeBSD release, time zone, terminal type, operator, change identifier, and exact script command separately. Capture relevant system logs and application results through their native mechanisms. Preserve an untouched original transcript, calculate a cryptographic hash after the session, and store the copy in the organization’s controlled evidence repository if chain-of-custody requirements apply.

Do not record the only view of a long-running process and assume the transcript has no gaps. The utility writes terminal data, not arbitrary background process output that is redirected elsewhere. If the process changes terminal modes or emits control sequences, the saved text may be difficult to read. The manual warns that full-screen applications can produce garbage in a typescript because the file emulates a hardcopy terminal rather than an addressable screen.

Choose an output path that the recording user controls. Do not run script as root in a world-writable directory with a predictable filename. A preexisting symlink or file can result in unintended writes or exposure. Prefer a newly created private directory, inspect the destination, and avoid sharing a typescript through a broad home-directory or web-server path.

The default behavior may replace an existing file; append mode -a preserves prior contents but mixes sessions and complicates attribution. Use a unique incident- or change-specific file and hash it after completion. If a recording must be redacted, make a sanitized copy and preserve the original under approved access controls. Redaction can invalidate evidentiary value, so record who performed it and how.

Transcripts can contain shell prompts that reveal usernames, working directories, customer names, internal DNS names, and command-line arguments. Even default output-only capture may contain secrets printed by a program. Review the output with a secret-scanning process before wider distribution. If a secret is exposed, treat it as compromised and rotate or revoke it according to policy rather than merely deleting the transcript.

Integrate with operations without creating false assurance

For an approved change, start recording before the first mutating command and stop only after the verification and rollback decision. Add concise annotations to the change ticket, not by typing secret-bearing notes into the recorded terminal. Keep commands idempotent where possible. If the change fails, preserve the first failure output and stop; a stream of repeated retries obscures root cause and may worsen the system.

Use the transcript together with native checks: config validators, service state, sockstat, storage status, application health probes, or a client transaction. A line that says “started” is not the same as a daemon accepting traffic. A command’s zero exit status means only that the command reported success according to its contract.

For recurring automation, prefer structured logs with explicit timestamps and exit codes. script is best for human-interactive sessions and for demonstrations where terminal behavior matters. It should not be used as a substitute for centralized logging, command authorization, application audit events, or a change-management record.

Acceptance and retention

Before declaring a recording complete, check that the file is readable by the intended reviewer and inaccessible to unintended users; that the session start, finish, and relevant verification appear; that timestamps are interpretable in the host’s time zone; and that the transcript does not contain unhandled secrets. Confirm any claimed system change using an independent state check.

Set retention based on the operational purpose and evidence policy. Delete ephemeral troubleshooting captures when their retention period expires, but preserve approved change evidence for the required interval. If the host is compromised, do not rely on its local typescript as the sole source of truth; collect evidence from independent logging and backup systems.

Used with that scope, script can make a terminal procedure reviewable and easier to reproduce. Its value comes from disciplined capture, access control, and independent verification, not from treating raw terminal output as a secure or complete record.

Related:

Sources:

Comments