<?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: Mark Marosi</title>
    <description>The latest articles on DEV Community by Mark Marosi (@mapika).</description>
    <link>https://dev.to/mapika</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%2F3860849%2Fb01d91ee-080a-47b3-bf4b-256521e43efe.png</url>
      <title>DEV Community: Mark Marosi</title>
      <link>https://dev.to/mapika</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mapika"/>
    <language>en</language>
    <item>
      <title>decider: one forward pass, typed decisions, calibrated probabilities</title>
      <dc:creator>Mark Marosi</dc:creator>
      <pubDate>Tue, 22 Sep 2026 12:11:22 +0000</pubDate>
      <link>https://dev.to/mapika/decider-one-forward-pass-typed-decisions-calibrated-probabilities-32c9</link>
      <guid>https://dev.to/mapika/decider-one-forward-pass-typed-decisions-calibrated-probabilities-32c9</guid>
      <description>&lt;p&gt;decider is a language model that does not generate text. It reads a state and a set of typed questions and returns, from one forward pass, a probability distribution for every question. There is no decoding, no parsing, and no output outside the options you defined.&lt;/p&gt;

&lt;p&gt;This post is what it does, how it is built, what the two public leaderboards say about it, and where it fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interface
&lt;/h2&gt;

&lt;p&gt;A typed decision is a question with a fixed answer set. Three kinds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choice&lt;/strong&gt; over 2 to 255 named options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score&lt;/strong&gt; over 2 to 10 described levels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noul&lt;/strong&gt;, the probability that the answer is yes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You pass a state (any JSON) and a dictionary of questions. You get back, per question, the chosen option, its confidence, and the full distribution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;decider-ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decider.infer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decider&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Decider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mapika/decider-2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# one CUDA GPU, bf16, about 4 GB
&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I was charged twice for order A-104. Please refund the duplicate.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refund_policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Duplicate charges are eligible for a refund.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;department&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instructions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which team should handle this?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;criteria&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;returns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exchanges, refunds, wrong or damaged items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;billing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;what&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Charges, invoices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not_for&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delivery&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;other&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refund_requested&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;noul&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instructions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Does `ticket.messages[0].text` request a refund?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frustration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instructions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How frustrated is the customer?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;criteria&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frustrated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;very frustrated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}})&lt;/span&gt;
&lt;span class="c1"&gt;# department: billing 0.56 (returns 0.44, other 0.00)
# refund_requested: 0.99
# frustration: level 1 of 3, 0.55
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plain form is &lt;code&gt;d.decide(state, [{"question": ..., "options": [...]}])&lt;/code&gt;. There is an HTTP server with the same wire format as TypeSafe's Jev API, so their SDKs work against a local &lt;code&gt;decider&lt;/code&gt; unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Every question is rendered into the prompt with lettered options and an answer slot. The model is run once. At each answer slot the logits of the option letters are read, divided by a fitted temperature, and passed through a softmax. That is the whole readout: the distribution is the model's own next-token belief over the letters, nothing is sampled and nothing is parsed.&lt;/p&gt;

&lt;p&gt;Because the readout is a single position in one forward pass, the cost of a decision is the cost of a prefill. On one B300 in bf16, batch of one, no CUDA graphs and no compilation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;median per decision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;decider-2b&lt;/td&gt;
&lt;td&gt;18 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decider-35b-a3b (3B active)&lt;/td&gt;
&lt;td&gt;41 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The CUDA-graph engine the package uses by default is faster than these plain numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The models
&lt;/h2&gt;

&lt;p&gt;All weights are on the Hub under Apache 2.0.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;base&lt;/th&gt;
&lt;th&gt;trained how&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;decider-0.8b&lt;/td&gt;
&lt;td&gt;Qwen3.5-0.8B-Base&lt;/td&gt;
&lt;td&gt;supervised&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decider-2b (v10)&lt;/td&gt;
&lt;td&gt;Qwen3.5-2B-Base&lt;/td&gt;
&lt;td&gt;supervised, then calibration-aware RL on live browser tasks and exact games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decider-2b-vision&lt;/td&gt;
&lt;td&gt;Qwen3.5-2B vision-language&lt;/td&gt;
&lt;td&gt;supervised on image questions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decider-35b-a3b&lt;/td&gt;
&lt;td&gt;Qwen3.5-35B-A3B-Base&lt;/td&gt;
&lt;td&gt;supervised, routed experts frozen, Muon on the block matrices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The training mixture is public datasets plus questions labelled by a local 27B teacher. Nothing was distilled from Jev. The mixture, the trainer and the evaluation code are in the repository; the recipe runs on one GPU for the small models.&lt;/p&gt;

&lt;p&gt;A result we did not plan for: the 35B's text weights loaded onto the vision-language version of its base answer image questions zero-shot. On 71 held-out image questions from The Cauldron it picks the gold answer 91.5% of the time, against 87.3% for the purpose-trained decider-2b-vision on the same rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the leaderboards say
&lt;/h2&gt;

&lt;p&gt;Two third-party leaderboards rank this model class. We did not run either; the numbers are theirs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision Index&lt;/strong&gt; (edition 0.1, 22 September 2026, by multimodalart) runs every open reproduction of Jev over the same 132,422 requests on one RTX PRO 6000, with unanswered requests counted as wrong. Its score is the mean of five capability areas. decider-35b-a3b is fourth at 54.3, behind Jev at 59.5 and two zero-training wrappers on stock models at 55.7 and 55.6. The gap is knowledge and reasoning: GPQA, GSM8K, CRUXEval and MMLU.&lt;/p&gt;

&lt;p&gt;The index also reports calibration: expected calibration error over ten confidence bins on 33 benchmarks, where confidence is the probability placed on the chosen option.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;entry&lt;/th&gt;
&lt;th&gt;calibration error (points)&lt;/th&gt;
&lt;th&gt;wrong at 95%+ confidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;decider-35b-a3b&lt;/td&gt;
&lt;td&gt;3.1&lt;/td&gt;
&lt;td&gt;0.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jevfire (stock Qwen3.8-27B)&lt;/td&gt;
&lt;td&gt;6.3&lt;/td&gt;
&lt;td&gt;1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jev 1.13.0&lt;/td&gt;
&lt;td&gt;6.5&lt;/td&gt;
&lt;td&gt;2.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decider-2b&lt;/td&gt;
&lt;td&gt;8.8&lt;/td&gt;
&lt;td&gt;0.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;decider-35b-a3b is the best-calibrated entry of the 32, Jev included. Its mean confidence is 0.673 and its accuracy on the same answers 0.675. Calibration is a readout on the site, not part of the score; a model can be well calibrated and wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JevBench&lt;/strong&gt; (read 21 September 2026) scores four axes. decider-35b-a3b is tenth of 36 at 68.9, pulled down by the cost axis; decider-2b is twenty-first at 64.6, pulled down by calibration on the hard tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits, stated plainly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One pass cannot do multi-step arithmetic.&lt;/strong&gt; There is no chain of thought, so GSM8K-type items and multi-hop chains are out of reach. Split such a judgment into several questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibration on hard items is the weak axis of the 2B.&lt;/strong&gt; Its top-label calibration error on JevBench's hard items is 0.30. The 35B's is 0.15.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge-heavy multiple choice.&lt;/strong&gt; The 2B improves little over its base on MMLU; the 35B closes part of the gap at three to four times the cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Games are not solved zero-shot.&lt;/strong&gt; The released models do not play Tetris, Breakout or Space Invaders from a text state; they sit at random level. The RL stage moved probability mass on exact games, not win rates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules written into the question are not followed at this size.&lt;/strong&gt; A convention has to be in the training data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;English only.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full measurement tables, including the regressions between versions, are in &lt;code&gt;docs/RESULTS.md&lt;/code&gt; and &lt;code&gt;docs/CHANGELOG.md&lt;/code&gt; in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;The base model sets the knowledge score. Under our readout, stock Qwen3.6-27B and Gemma-4-26B-A4B-it both read above our trained 35B on a sample of the index, so the next series will start from post-trained bases, keep the readout and the calibration, and train only where training beats the stock model, which on our measurements is the 2B and 4B sizes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code and docs: &lt;a href="https://github.com/Mapika/decider" rel="noopener noreferrer"&gt;https://github.com/Mapika/decider&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Weights: &lt;a href="https://huggingface.co/Mapika" rel="noopener noreferrer"&gt;https://huggingface.co/Mapika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Package: &lt;code&gt;pip install decider-ai&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an independent project, not affiliated with or endorsed by TypeSafe AI.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>opensource</category>
      <category>python</category>
      <category>llm</category>
    </item>
    <item>
      <title>I replaced lsof, ss, and netstat with a single Rust binary</title>
      <dc:creator>Mark Marosi</dc:creator>
      <pubDate>Sat, 04 Apr 2026 10:51:53 +0000</pubDate>
      <link>https://dev.to/mapika/i-replaced-lsof-ss-and-netstat-with-a-single-rust-binary-36i</link>
      <guid>https://dev.to/mapika/i-replaced-lsof-ss-and-netstat-with-a-single-rust-binary-36i</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every developer has been here: something is hogging port 3000 and you need to find out what.&lt;/p&gt;

&lt;p&gt;On Linux you try &lt;code&gt;ss -tlnp | grep 3000&lt;/code&gt;. On macOS it's &lt;code&gt;lsof -i :3000&lt;/code&gt;. On Windows... good luck. Each gives different output, different flags, and none of them tell you how long the process has been running, how much memory it's eating, or whether it's a Docker container.&lt;/p&gt;

&lt;p&gt;I got tired of this. So I built &lt;a href="https://github.com/Mapika/portview" rel="noopener noreferrer"&gt;portview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One command, everything you need
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;portview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Every listening port, the process behind it, PID, user, uptime, memory usage, and the full command -- in a colored table. Cross-platform. ~1.3 MB single binary. Zero runtime dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT  PROTO  PID    USER   PROCESS   UPTIME   MEM     COMMAND
3000  TCP    48291  mark   node      3h 12m   248 MB  next dev
5432  TCP    1203   pg     postgres  14d 2h   38 MB   /usr/lib/postgresql/16/bin/postgres
6379  TCP    1198   redis  redis     14d 2h   12 MB   redis-server *:6379
8080  TCP    51002  mark   python3   22m      45 MB   uvicorn main:app --port 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No parsing &lt;code&gt;lsof&lt;/code&gt; output through &lt;code&gt;awk&lt;/code&gt;. No remembering whether it's &lt;code&gt;-tlnp&lt;/code&gt; or &lt;code&gt;-tulpn&lt;/code&gt;. Just &lt;code&gt;portview&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  But I didn't stop there
&lt;/h2&gt;

&lt;p&gt;A port viewer that just lists ports isn't worth writing about. Here's what makes portview different:&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactive TUI with tree view
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;portview watch&lt;/code&gt; opens a live-refreshing TUI. Navigate with &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt;, press &lt;code&gt;Enter&lt;/code&gt; to inspect a port in detail (full command, working directory, child processes, open connections), press &lt;code&gt;d&lt;/code&gt; to kill it.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;t&lt;/code&gt; to toggle &lt;strong&gt;tree view&lt;/strong&gt; -- it groups child processes under their parents so you can see which workers belong to which master process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PROCESS
node
├── node (worker)
├── node (worker)
└── node (worker)
postgres
└── postgres: autovacuum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker as a first-class citizen
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;portview watch --docker&lt;/code&gt; shows Docker containers as rows in the table. No more running &lt;code&gt;docker ps&lt;/code&gt; in a separate terminal and cross-referencing ports. Press &lt;code&gt;d&lt;/code&gt; on a container row to Stop, Restart, or tail Logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  portview doctor
&lt;/h3&gt;

&lt;p&gt;This is my favorite feature. &lt;code&gt;portview doctor&lt;/code&gt; runs five diagnostic checks on your ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;portview doctor
&lt;span class="go"&gt;  ✓ No port conflicts
  ✗ postgres is listening on 0.0.0.0:5432 -- consider binding to 127.0.0.1
  ✓ No Docker-host conflicts
  ✓ No stale connections
  ✓ No high-resource listeners
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It catches: port conflicts (two processes fighting over the same port), databases exposed on all interfaces, Docker port collisions with host processes, TIME_WAIT/CLOSE_WAIT pileups, and memory hogs. Use &lt;code&gt;portview doctor --json&lt;/code&gt; with exit code 1 on errors for CI.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH remote mode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;portview ssh user@server          &lt;span class="c"&gt;# scan remote ports&lt;/span&gt;
portview ssh user@server watch    &lt;span class="c"&gt;# full remote TUI&lt;/span&gt;
portview ssh user@server doctor   &lt;span class="c"&gt;# remote diagnostics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shells out to your system &lt;code&gt;ssh&lt;/code&gt; binary (inherits your config, keys, ProxyJump, everything), runs &lt;code&gt;portview --json&lt;/code&gt; on the remote host, and renders the output locally. Kill actions in the remote TUI are forwarded over SSH. No agents, no daemons, no new ports to open.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;No shelling out to &lt;code&gt;lsof&lt;/code&gt; or &lt;code&gt;ss&lt;/code&gt;. portview reads directly from the OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt;: Parses &lt;code&gt;/proc/net/tcp&lt;/code&gt;, maps inodes to PIDs via &lt;code&gt;/proc/*/fd/&lt;/code&gt;, reads process metadata from &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/status&lt;/code&gt; and &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/stat&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: Uses &lt;code&gt;libproc&lt;/code&gt; FFI (&lt;code&gt;proc_listpids&lt;/code&gt;, &lt;code&gt;proc_pidfdinfo&lt;/code&gt;, &lt;code&gt;proc_pidinfo&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: Uses &lt;code&gt;iphlpapi&lt;/code&gt; (&lt;code&gt;GetExtendedTcpTable&lt;/code&gt;) and &lt;code&gt;kernel32&lt;/code&gt; (&lt;code&gt;CreateToolhelp32Snapshot&lt;/code&gt;, &lt;code&gt;GetProcessTimes&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why it's fast -- there's no subprocess overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/mapika/portview/main/install.sh | sh   &lt;span class="c"&gt;# Linux/macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;mapika/tap/portview                                                      &lt;span class="c"&gt;# Homebrew&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;portview                                                                &lt;span class="c"&gt;# Cargo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or grab a binary from the &lt;a href="https://github.com/Mapika/portview/releases" rel="noopener noreferrer"&gt;releases page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The codebase is ~6K lines of Rust. MIT licensed. Contributions welcome.&lt;/p&gt;

&lt;p&gt;If you've ever been frustrated by &lt;code&gt;lsof&lt;/code&gt;, give it a try and let me know what you think: &lt;a href="https://github.com/Mapika/portview" rel="noopener noreferrer"&gt;github.com/Mapika/portview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cli</category>
      <category>linux</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
