<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zhaoqi wu</title>
    <description>The latest articles on DEV Community by zhaoqi wu (@zhaoqi_wu_dabe36fbd662792).</description>
    <link>https://dev.to/zhaoqi_wu_dabe36fbd662792</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4038120%2F0ba55057-453f-4628-a66b-163e2b5830fe.png</url>
      <title>DEV Community: zhaoqi wu</title>
      <link>https://dev.to/zhaoqi_wu_dabe36fbd662792</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zhaoqi_wu_dabe36fbd662792"/>
    <language>en</language>
    <item>
      <title>How I Built a Privacy-First Codex Usage Dashboard on macOS</title>
      <dc:creator>zhaoqi wu</dc:creator>
      <pubDate>Mon, 20 Jul 2026 12:16:39 +0000</pubDate>
      <link>https://dev.to/zhaoqi_wu_dabe36fbd662792/how-i-built-a-privacy-first-codex-usage-dashboard-on-macos-4180</link>
      <guid>https://dev.to/zhaoqi_wu_dabe36fbd662792/how-i-built-a-privacy-first-codex-usage-dashboard-on-macos-4180</guid>
      <description>&lt;p&gt;A Codex usage dashboard can answer more than “how many tokens did I use?” The harder question is how to inspect usage across sessions without turning a local analytics tool into another place that stores prompts, responses, terminal output, and private paths.&lt;/p&gt;

&lt;p&gt;I ran into that problem while building UsageCove Local, a macOS dashboard for selected metadata from supported Codex session logs. This article explains the design decisions behind it: stream the logs, retain only an explicit allowlist of fields, keep the service on loopback, and state clearly what the resulting numbers cannot prove.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a narrow threat model
&lt;/h2&gt;

&lt;p&gt;Local session logs may contain much more than token counts. Depending on the event, a raw line can include conversation text, tool arguments, terminal output, working directories, repository details, or authentication-related data.&lt;/p&gt;

&lt;p&gt;For a usage dashboard, most of that content is unnecessary. The useful inputs are much smaller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;token counts and timestamps;&lt;/li&gt;
&lt;li&gt;model names;&lt;/li&gt;
&lt;li&gt;session identifiers;&lt;/li&gt;
&lt;li&gt;the final component of a working-directory label;&lt;/li&gt;
&lt;li&gt;quota-window observations found in supported events;&lt;/li&gt;
&lt;li&gt;timing and explicit failure status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That became the core rule: if a field is not required for a visible aggregate, it should not survive the scan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stream Codex session logs instead of loading them wholesale
&lt;/h2&gt;

&lt;p&gt;The scanner uses a line stream for the JSONL files. Each line is parsed long enough to inspect supported event fields, then only selected metadata is added to the aggregate state.&lt;/p&gt;

&lt;p&gt;The data flow is intentionally short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Codex JSONL files (read-only)
        ↓
line-by-line parser
        ↓
allowlisted metadata extractor
        ↓
aggregate cache
        ↓
127.0.0.1 dashboard → optional user-triggered CSV
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has two benefits. First, memory use does not grow with the full history in the same way as reading every file into one large structure. Second, it creates a clear retention boundary: raw log lines and conversation text are not written into the cache.&lt;/p&gt;

&lt;p&gt;The cache still contains data and should not be described as anonymous. It includes token totals, timestamps, session identifiers, model names, display labels, failure flags, and quota observations. The goal is data minimization, not magic disappearance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat local HTTP as a security boundary
&lt;/h2&gt;

&lt;p&gt;“It only runs locally” is not enough by itself. Browsers, other local processes, and unexpected Host values can still reach a careless local server.&lt;/p&gt;

&lt;p&gt;The dashboard therefore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;binds to a random port on &lt;code&gt;127.0.0.1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;creates a random session token;&lt;/li&gt;
&lt;li&gt;exchanges the initial token for an &lt;code&gt;HttpOnly&lt;/code&gt;, &lt;code&gt;SameSite=Strict&lt;/code&gt; cookie;&lt;/li&gt;
&lt;li&gt;rejects unexpected Host values;&lt;/li&gt;
&lt;li&gt;does not enable cross-origin access;&lt;/li&gt;
&lt;li&gt;sends no telemetry and makes no automatic request to analytics, pricing, licensing, or update services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These controls do not make every local threat disappear, but they keep the attack surface aligned with the product’s job: read local usage metadata and show it to the current user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export less, and export only on demand
&lt;/h2&gt;

&lt;p&gt;CSV export is useful for comparing sessions or doing a longer review, but it also creates a new file that users may share.&lt;/p&gt;

&lt;p&gt;For that reason, export happens only after an explicit click. The CSV contains filtered aggregate rows rather than prompts, responses, tool arguments, terminal output, credentials, full working-directory paths, or raw JSONL lines.&lt;/p&gt;

&lt;p&gt;Even aggregate labels can be sensitive. A final directory name may reveal a client or project name, so the interface tells users to review the CSV before sharing it. “No prompt text” does not mean “safe to publish without checking.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate observation from attribution
&lt;/h2&gt;

&lt;p&gt;A local Codex usage dashboard can show when token usage increased. It can also rank high-usage sessions and models inside a selected time range.&lt;/p&gt;

&lt;p&gt;Those are two separate views. They do not, by themselves, prove that a particular session caused a particular spike. Making that causal claim would require event-level correlation and stronger evidence than a range ranking provides.&lt;/p&gt;

&lt;p&gt;The same restraint applies to quota and cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quota values are delayed observations found in local logs, not official real-time status;&lt;/li&gt;
&lt;li&gt;cost values are API-equivalent reference estimates, not a ChatGPT or Codex subscription bill;&lt;/li&gt;
&lt;li&gt;explicit failure events are not the same thing as a complete failure rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clear limitations make an analytics tool more useful because users know which decisions the data can support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build support diagnostics around omission
&lt;/h2&gt;

&lt;p&gt;Support is another place where private data can leak. Asking a user to email a raw session log would defeat the product’s privacy boundary.&lt;/p&gt;

&lt;p&gt;The support command therefore reports runtime and aggregate diagnostics designed to omit session content and private paths. Release packaging also checks for logs, authentication files, credential patterns, and private paths before creating the customer archive.&lt;/p&gt;

&lt;p&gt;The broader lesson is simple: privacy should cover debugging and distribution, not only the main dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would change next
&lt;/h2&gt;

&lt;p&gt;The current Early Access build still has meaningful friction: macOS 14 or later, Node.js 20 or later, and Terminal are required. The dashboard UI is Simplified Chinese, although the documentation is bilingual. It is not yet a signed, double-click native Mac app.&lt;/p&gt;

&lt;p&gt;My next priorities are reducing installation friction and improving how the dashboard explains the difference between a timestamped spike and a session ranking. An English interface would also make external technical feedback much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the synthetic-data workflow
&lt;/h2&gt;

&lt;p&gt;Disclosure: I am the developer of UsageCove Local. The &lt;a href="https://2941383613-png.itch.io/usagecove-local" rel="noopener noreferrer"&gt;product page and free synthetic-data Demo&lt;/a&gt; let you inspect the interface and CSV workflow without scanning local session logs. The full Early Access build is US$6.99 or more.&lt;/p&gt;

&lt;p&gt;UsageCove Local is independent and unofficial, with no affiliation or endorsement by OpenAI. Development includes AI-assisted code, as disclosed on the product page.&lt;/p&gt;

&lt;p&gt;That is the boundary I want this Codex usage dashboard to keep as it evolves.&lt;/p&gt;

&lt;p&gt;If you build local developer analytics, which aggregate fields would you retain—and which would you refuse to store?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>privacy</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
