[GH-ISSUE #4907] [Bug] inspect_pcap_capture reports capture_start and capture_end in host-local time without a zone #3046

Closed
opened 2026-10-31 00:35:27 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @mertkayacs on GitHub (Oct 17, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4907

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

inspect_pcap_capture returns capture_start and capture_end as the text capinfos prints by default, for example 2026-10-11 14:13:40.608505. That spelling is the packet time converted to the analyzing host's local zone, with no Z or offset. The same capture produces different strings on hosts in different zones, and no consumer can turn the string back into an instant.

Reproduction

  1. Take any capture with at least one packet.
  2. Run rea inspect-pcap-capture <file> --json twice, once with TZ=UTC and once with TZ=Europe/Berlin (or any other zone) in the environment.
  3. Compare capture_start: the two runs differ by the zone offset and neither carries a marker.

Source path: src/pcap/PcapProvider.ts runs capinfos -M -- <snapshot>; src/pcap/PcapCommand.ts pins LC_ALL=C and LANG=C but inherits TZ; src/pcap/PcapOutput.ts stores the Earliest packet time text as is. In Wireshark, capinfos.c absolute_time_string calls format_nstime_as_iso8601(..., local = true, ...), which uses localtime and appends Z only when local is false (wsutil/to_str.c). With -S it calls display_epoch_time, which prints exact epoch seconds with the capture's fractional precision.

Expected behavior

One capture yields the same capture_start and capture_end bytes on every host, with an explicit zone and the producer's exact precision, in line with REA's own timestamp contracts that require an explicit UTC offset (offsetDateTimeSchema) and with AGENTS.md's rule that identity values stay separate from display formatting. Running capinfos with -S and projecting the epoch value to an RFC 3339 UTC instant (2026-10-11T14:13:40.608505Z) achieves this without rounding.

Impact and scope

Every inspect_pcap_capture result on a non-UTC host; evidence comparison and digests across hosts diverge on these two fields; the strings cannot be parsed as instants. Frequency: deterministic. Executed with capinfos 4.2.2 (Ubuntu 24.04, Wireshark 4.2.2-1.1build3) on a two-packet capture written for the report: see the output below.

REA version

6.5.0, source 2fb5e204a3

Runtime and platform

Node 24.18.0, Linux x64

Target and artifact context

Any pcap or pcapng file with at least one packet; no provider beyond capinfos.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Executed, capinfos 4.2.2, the same two-packet capture, LC_ALL=C as REA sets it:

$ TZ=Europe/Istanbul capinfos -M -- one.pcap | grep "packet time"
First packet time:   2026-10-11 17:13:40.608505
Last packet time:    2026-10-11 17:13:40.981978
$ TZ=UTC capinfos -M -- one.pcap | grep "packet time"
First packet time:   2026-10-11 14:13:40.608505
Last packet time:    2026-10-11 14:13:40.981978
$ TZ=Europe/Istanbul capinfos -M -S -- one.pcap | grep "packet time"
First packet time:   1791728020.608505
Last packet time:    1791728020.981978

The default spelling moves with TZ and carries no marker; -S prints the same epoch value under both zones, which identifies the instant 2026-10-11T14:13:40.608505Z exactly. REA stores the default spelling as capture_start (the parser accepts both the First/Last and Earliest/Latest labels).

Typed outcome or recovery expected

capture_start/capture_end as RFC 3339 UTC strings; null for n/a (empty capture) and for any spelling that does not name an instant.

Analysis

Fix prepared: run capinfos -M -S and project the epoch text at the capinfos metadata owner, keeping every fractional digit. Regression coverage at the unit, provider and MCP boundaries.

Originally created by @mertkayacs on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4907 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior `inspect_pcap_capture` returns `capture_start` and `capture_end` as the text capinfos prints by default, for example `2026-10-11 14:13:40.608505`. That spelling is the packet time converted to the analyzing host's local zone, with no `Z` or offset. The same capture produces different strings on hosts in different zones, and no consumer can turn the string back into an instant. ### Reproduction 1. Take any capture with at least one packet. 2. Run `rea inspect-pcap-capture <file> --json` twice, once with `TZ=UTC` and once with `TZ=Europe/Berlin` (or any other zone) in the environment. 3. Compare `capture_start`: the two runs differ by the zone offset and neither carries a marker. Source path: `src/pcap/PcapProvider.ts` runs `capinfos -M -- <snapshot>`; `src/pcap/PcapCommand.ts` pins `LC_ALL=C` and `LANG=C` but inherits `TZ`; `src/pcap/PcapOutput.ts` stores the `Earliest packet time` text as is. In Wireshark, `capinfos.c` `absolute_time_string` calls `format_nstime_as_iso8601(..., local = true, ...)`, which uses `localtime` and appends `Z` only when `local` is false (`wsutil/to_str.c`). With `-S` it calls `display_epoch_time`, which prints exact epoch seconds with the capture's fractional precision. ### Expected behavior One capture yields the same `capture_start` and `capture_end` bytes on every host, with an explicit zone and the producer's exact precision, in line with REA's own timestamp contracts that require an explicit UTC offset (`offsetDateTimeSchema`) and with AGENTS.md's rule that identity values stay separate from display formatting. Running capinfos with `-S` and projecting the epoch value to an RFC 3339 UTC instant (`2026-10-11T14:13:40.608505Z`) achieves this without rounding. ### Impact and scope Every `inspect_pcap_capture` result on a non-UTC host; evidence comparison and digests across hosts diverge on these two fields; the strings cannot be parsed as instants. Frequency: deterministic. Executed with capinfos 4.2.2 (Ubuntu 24.04, Wireshark 4.2.2-1.1build3) on a two-packet capture written for the report: see the output below. ### REA version 6.5.0, source 2fb5e204a35723f6f843d56ca47b0db94d8fcc44 ### Runtime and platform Node 24.18.0, Linux x64 ### Target and artifact context Any pcap or pcapng file with at least one packet; no provider beyond capinfos. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Executed, capinfos 4.2.2, the same two-packet capture, `LC_ALL=C` as REA sets it: ``` $ TZ=Europe/Istanbul capinfos -M -- one.pcap | grep "packet time" First packet time: 2026-10-11 17:13:40.608505 Last packet time: 2026-10-11 17:13:40.981978 $ TZ=UTC capinfos -M -- one.pcap | grep "packet time" First packet time: 2026-10-11 14:13:40.608505 Last packet time: 2026-10-11 14:13:40.981978 $ TZ=Europe/Istanbul capinfos -M -S -- one.pcap | grep "packet time" First packet time: 1791728020.608505 Last packet time: 1791728020.981978 ``` The default spelling moves with `TZ` and carries no marker; `-S` prints the same epoch value under both zones, which identifies the instant `2026-10-11T14:13:40.608505Z` exactly. REA stores the default spelling as `capture_start` (the parser accepts both the `First`/`Last` and `Earliest`/`Latest` labels). ### Typed outcome or recovery expected `capture_start`/`capture_end` as RFC 3339 UTC strings; `null` for `n/a` (empty capture) and for any spelling that does not name an instant. ### Analysis Fix prepared: run `capinfos -M -S` and project the epoch text at the capinfos metadata owner, keeping every fractional digit. Regression coverage at the unit, provider and MCP boundaries.
Author
Owner

@mertkayacs commented on GitHub (Oct 17, 2026):

Fix with regression tests: https://github.com/morluto/rea/pull/4909. REA runs capinfos with -S and projects the epoch value to an RFC 3339 UTC instant, keeping every fractional digit; n/a and zone-less spellings stay null.

<!-- gh-comment-id:6242902615 --> @mertkayacs commented on GitHub (Oct 17, 2026): Fix with regression tests: https://github.com/morluto/rea/pull/4909. REA runs capinfos with -S and projects the epoch value to an RFC 3339 UTC instant, keeping every fractional digit; n/a and zone-less spellings stay null.
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Fixed on main in 796faa0e4 (provider/parser, regression coverage, docs) and d205c528e (independent real-engine verifier oracle). Adapted from @mertkayacs's PR #4909 with coauthor attribution.

The producing command now requests capinfos -M -S; one metadata decoder projects exact epoch text to UTC without rounding fractional digits. Signed fractions, nanoseconds and years 0000–9999 are covered; absent, malformed, zone-less or unrepresentable timestamps remain unknown. Parsing is bounded to the producer's 64-bit seconds/nanosecond representation. The verifier now checks against the independently authored PCAP record timestamps rather than copying capinfos local display output.

Executed on macOS arm64 / Node 24.18.0:

  • Baseline source parser: 3 failures, 23 passing controls across two files.
  • Final focused checks: 47 passed, 24 platform skips across five files. MCP Evidence projection uses a fixture provider; subprocess tool probes use inert stubs.
  • check:fast, normal commit/pre-push hooks, syntax check and discovery (1563 files exactly once) passed. Two read-only source reviews found no blocker.

Research: Wireshark v4.2.2 capinfos.c, wsutil/to_str.c, wsutil/nstime.c checked alongside DeepWiki context. Source: https://github.com/wireshark/wireshark/blob/v4.2.2/wsutil/to_str.c#L846

Limits: no installed capinfos/tshark or live Linux/Windows capture execution here. Pre-epoch checks establish the signed epoch-text arithmetic, not real negative-capture qualification. The contributor's reported Linux execution is not counted as our validation. Heavy CPU test/profiling remain stopped. No full-suite or performance claim.

<!-- gh-comment-id:6243225722 --> @morluto commented on GitHub (Oct 17, 2026): Fixed on main in 796faa0e4 (provider/parser, regression coverage, docs) and d205c528e (independent real-engine verifier oracle). Adapted from @mertkayacs's PR #4909 with coauthor attribution. The producing command now requests `capinfos -M -S`; one metadata decoder projects exact epoch text to UTC without rounding fractional digits. Signed fractions, nanoseconds and years 0000–9999 are covered; absent, malformed, zone-less or unrepresentable timestamps remain unknown. Parsing is bounded to the producer's 64-bit seconds/nanosecond representation. The verifier now checks against the independently authored PCAP record timestamps rather than copying capinfos local display output. Executed on macOS arm64 / Node 24.18.0: - Baseline source parser: 3 failures, 23 passing controls across two files. - Final focused checks: 47 passed, 24 platform skips across five files. MCP Evidence projection uses a fixture provider; subprocess tool probes use inert stubs. - `check:fast`, normal commit/pre-push hooks, syntax check and discovery (1563 files exactly once) passed. Two read-only source reviews found no blocker. Research: Wireshark v4.2.2 `capinfos.c`, `wsutil/to_str.c`, `wsutil/nstime.c` checked alongside DeepWiki context. Source: https://github.com/wireshark/wireshark/blob/v4.2.2/wsutil/to_str.c#L846 Limits: no installed capinfos/tshark or live Linux/Windows capture execution here. Pre-epoch checks establish the signed epoch-text arithmetic, not real negative-capture qualification. The contributor's reported Linux execution is not counted as our validation. Heavy CPU test/profiling remain stopped. No full-suite or performance claim.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3046
No description provided.