<?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: XogtaTech</title>
    <description>The latest articles on DEV Community by XogtaTech (@vrpot).</description>
    <link>https://dev.to/vrpot</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%2F4090333%2F86f682af-aa29-49e7-a59a-43dedf0f1e72.png</url>
      <title>DEV Community: XogtaTech</title>
      <link>https://dev.to/vrpot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vrpot"/>
    <language>en</language>
    <item>
      <title>Snook: The Protected Ingestion Plane</title>
      <dc:creator>XogtaTech</dc:creator>
      <pubDate>Sun, 23 Aug 2026 02:11:42 +0000</pubDate>
      <link>https://dev.to/vrpot/snook-the-protected-ingestion-plane-5fda</link>
      <guid>https://dev.to/vrpot/snook-the-protected-ingestion-plane-5fda</guid>
      <description>&lt;p&gt;&lt;em&gt;Move operational data into open lakehouse tables without making raw data the&lt;br&gt;
price of using cloud analytics.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Modern ingestion usually begins with a network diagram. A cloud service,&lt;br&gt;
integration runtime, or distributed processing cluster needs access to each&lt;br&gt;
database, file server, application host, stream, or legacy platform. That&lt;br&gt;
brings credentials, inbound routes, vendor allowlists, staging areas, and a&lt;br&gt;
period in which sensitive data has moved but has not yet been protected.&lt;/p&gt;

&lt;p&gt;Snook starts from a different boundary: execute beside the source, protect each&lt;br&gt;
sensitive field before the first landed file, and send protected analytical&lt;br&gt;
data plus verifiable evidence onward.&lt;/p&gt;

&lt;p&gt;Snook is a protected ingestion product that owns the path from source evidence&lt;br&gt;
to an open table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database, file, stream, or mainframe source
  -&amp;gt; schema and contract
  -&amp;gt; bounded extraction or decode
  -&amp;gt; quality and protection
  -&amp;gt; Parquet
  -&amp;gt; Iceberg
  -&amp;gt; manifest and lineage evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scheduler and lakehouse may remain in the cloud. Raw-data access does not&lt;br&gt;
have to. Snook supports relational databases, structured files, bounded stream&lt;br&gt;
windows, COBOL-described datasets, and DB2 unloads through the same contract,&lt;br&gt;
protection, Parquet, Iceberg, manifest, and lineage model.&lt;/p&gt;
&lt;h2&gt;
  
  
  Walk Into the Worst Case
&lt;/h2&gt;

&lt;p&gt;A friendly product demonstration would start with CSV, Parquet, or a simple SQL&lt;br&gt;
table. Those sources matter, but they avoid the difficult question: does the&lt;br&gt;
architecture still hold when extraction is inseparable from binary framing,&lt;br&gt;
schema interpretation, character conversion, and exact decimal decoding?&lt;/p&gt;

&lt;p&gt;That is why this test walks into mainframe data. Mainframe is an example, not&lt;br&gt;
the product boundary, and it gives us three increasingly demanding source&lt;br&gt;
paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;COBOL-described datasets.&lt;/strong&gt; Fixed, variable, and blocked EBCDIC records are
decoded from a copybook, including packed decimal, binary numerics,
&lt;code&gt;REDEFINES&lt;/code&gt;, &lt;code&gt;OCCURS&lt;/code&gt;, and hierarchical record structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DB2 for z/OS UNLOAD datasets.&lt;/strong&gt; Snook reads the binary UNLOAD data together
with the &lt;code&gt;SYSPUNCH&lt;/code&gt; LOAD control card. The LOAD card is the schema source:
its positions, DB2 types, and null indicators drive the same decode,
protection, Parquet, and evidence path without a database connection or
copybook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live DB2 extraction.&lt;/strong&gt; When direct access is appropriate, Snook can read
DB2 through IBM's CLI driver, with bounded incremental windows and parallel
numeric range extraction. Credentials remain references and are masked from
manifests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same contract model covers PostgreSQL, MySQL and MariaDB, SQL Server,&lt;br&gt;
Oracle, Sybase ASE, structured files, and bounded Kafka windows. Mainframe is&lt;br&gt;
where we chose to stress the architecture, not the market category Snook is&lt;br&gt;
limited to.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prove the Cat, Then Carry the Kitten
&lt;/h2&gt;

&lt;p&gt;There is a useful engineering test for an ingestion architecture: if a cat can&lt;br&gt;
pass through the opening, a kitten can pass through it too.&lt;/p&gt;

&lt;p&gt;EBCDIC is the cat. It is the path most likely to be excluded from a friendly&lt;br&gt;
benchmark because every byte requires interpretation. Record boundaries may be&lt;br&gt;
fixed, RDW-framed, or block-framed. Text needs a host code page. Decimal values&lt;br&gt;
may be zoned or packed. Binary numbers, overlays, repeated groups, and&lt;br&gt;
record-type hierarchies all have to be interpreted before a columnar writer can&lt;br&gt;
begin its work.&lt;/p&gt;

&lt;p&gt;A CSV copy, a Parquet relocation, or a straightforward SQL result would make a&lt;br&gt;
better-looking demonstration. It would prove less. We deliberately tested the&lt;br&gt;
larger animal: a wide EBCDIC dataset with protection in the hot path and an&lt;br&gt;
Iceberg commit at the end.&lt;/p&gt;

&lt;p&gt;That does not make every easier source identical. It demonstrates that the&lt;br&gt;
bounded execution, protection, Parquet, catalog, and evidence architecture can&lt;br&gt;
carry the least forgiving source shape without requiring a cluster.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Product Boundary
&lt;/h2&gt;

&lt;p&gt;Snook brings a small Go execution layer to the source environment. A versioned&lt;br&gt;
contract controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source identity and schema source;&lt;/li&gt;
&lt;li&gt;record format, code page, and decode behavior;&lt;/li&gt;
&lt;li&gt;destination table and load mode;&lt;/li&gt;
&lt;li&gt;schema evolution and quality rules;&lt;/li&gt;
&lt;li&gt;field classification and protection mode;&lt;/li&gt;
&lt;li&gt;batch, worker, memory, and Custody concurrency budgets; and&lt;/li&gt;
&lt;li&gt;lineage and evidence emitted by the run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI is the authoring and review surface for that contract. Operators can&lt;br&gt;
sample through the production decoder, inspect discovered fields and types,&lt;br&gt;
review classification evidence, assign protection, set execution limits, and&lt;br&gt;
validate the exact artifact that the runner will execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rgypu7imbab6jkdbx9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5rgypu7imbab6jkdbx9l.png" alt="Snook authors a copybook source from a local EBCDIC file" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the measured EBCDIC run, the UI discovered all 100 fields and displayed&lt;br&gt;
source and destination types together. Classification remained reviewable. The&lt;br&gt;
classifier identified the card and phone fields cleanly; the synthetic Canadian&lt;br&gt;
SIN also resembled a US routing number. Snook surfaced that disagreement rather&lt;br&gt;
than silently turning a probabilistic guess into security policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn42meulf5x96st26z20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn42meulf5x96st26z20.png" alt="The authoring UI discovers all 100 source columns" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast binaries are useful. A reproducible, inspectable decision surface is what&lt;br&gt;
makes them an enterprise product.&lt;/p&gt;
&lt;h2&gt;
  
  
  Protection Before Landing
&lt;/h2&gt;

&lt;p&gt;The execution order is deliberate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source bytes -&amp;gt; decode/extract -&amp;gt; quality -&amp;gt; protection -&amp;gt; Parquet -&amp;gt; Iceberg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cleartext exists only in the in-flight batch. Snook does not write raw Parquet&lt;br&gt;
and clean it up in a second job. Data on the destination side of the boundary is&lt;br&gt;
already protected.&lt;/p&gt;

&lt;p&gt;The benchmark contract made three different decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;card_number&lt;/code&gt; used a Snook Custody &lt;code&gt;vault-token&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sin&lt;/code&gt; used FF1 format-preserving encryption; and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;phone&lt;/code&gt; used FF1 format-preserving encryption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FF1 retains shape, which matters when downstream schemas cannot absorb a width&lt;br&gt;
or type change. It remains reversible with the key and should not be presented&lt;br&gt;
as automatic PCI descoping.&lt;/p&gt;

&lt;p&gt;A Custody token is a random surrogate. It preserves no meaningful portion of&lt;br&gt;
the card number and cannot be reversed without Custody. Snook Custody is the&lt;br&gt;
licensed vault itself, not an adapter to a separate vault product.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Raw value leaves the source?&lt;/th&gt;
&lt;th&gt;Preserves format?&lt;/th&gt;
&lt;th&gt;Security boundary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw cloud pull&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Protection occurs after movement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local FF1&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Reversible cryptographic protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snook Custody token&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Random token with isolated recovery capability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Protection is fail-closed. If a contract requires Custody and Custody is&lt;br&gt;
unavailable, the run stops. It does not land cleartext or silently substitute a&lt;br&gt;
weaker mode.&lt;/p&gt;

&lt;p&gt;Most of the platform is open source. &lt;strong&gt;Snook Custody is the licensed component&lt;br&gt;
and is not planned for open-source release.&lt;/strong&gt; An open-source-only deployment can&lt;br&gt;
choose FF1 for a PCI field, but the contract records the choice and requires an&lt;br&gt;
explicit acknowledgement of the different security boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deliberately Difficult Test
&lt;/h2&gt;

&lt;p&gt;The source was a 24.45 GB synthetic EBCDIC file containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50,000,000 fixed-length records;&lt;/li&gt;
&lt;li&gt;489 bytes per record in code page 037;&lt;/li&gt;
&lt;li&gt;100 COBOL fields per record;&lt;/li&gt;
&lt;li&gt;one million distinct synthetic card numbers repeated across the file;&lt;/li&gt;
&lt;li&gt;a synthetic SIN and phone number on every row;&lt;/li&gt;
&lt;li&gt;30 text fields;&lt;/li&gt;
&lt;li&gt;40 display-numeric fields; and&lt;/li&gt;
&lt;li&gt;26 signed packed-decimal fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generator and copybook shared one layout definition. Independent boundary&lt;br&gt;
checks verified a file size of exactly 24,450,000,000 bytes, no remainder&lt;br&gt;
against the 489-byte record length, and a card sequence repeating at exactly one&lt;br&gt;
million unique values.&lt;/p&gt;

&lt;p&gt;This was a COBOL-copybook EBCDIC test, not a DB2 UNLOAD benchmark. DB2 UNLOAD&lt;br&gt;
uses its LOAD control card as the schema source and joins the same downstream&lt;br&gt;
protection, Parquet, Iceberg, manifest, and lineage path. Its throughput should&lt;br&gt;
be measured separately rather than inferred from this result.&lt;/p&gt;

&lt;p&gt;The UI-authored contract selected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;append into &lt;code&gt;raw.wide_customers_50m_auto&lt;/code&gt; through an Iceberg REST catalog;&lt;/li&gt;
&lt;li&gt;first-run table creation and additive schema evolution;&lt;/li&gt;
&lt;li&gt;eight balanced decode ranges and eight local protection workers;&lt;/li&gt;
&lt;li&gt;batches of 8,192 rows;&lt;/li&gt;
&lt;li&gt;four Custody requests globally in flight;&lt;/li&gt;
&lt;li&gt;a maximum Custody request window of 10,000 values; and&lt;/li&gt;
&lt;li&gt;fail-closed protection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CPU worker and Custody budgets are separate. Increasing local decode&lt;br&gt;
parallelism cannot multiply load on the shared security service. With the&lt;br&gt;
8,192-row batch inside the 10,000-value request window, the client used four&lt;br&gt;
requests of roughly 2,048 values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9q34rm1wv5qp1zbadxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9q34rm1wv5qp1zbadxa.png" alt="The complete destination and execution controls in Snook" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
The contract passed &lt;code&gt;snook-run validate&lt;/code&gt; across all 100 fields and produced a&lt;br&gt;
stable SHA-256 fingerprint. The artifact reviewed in the UI was the artifact&lt;br&gt;
executed by the runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  50 Million Rows: Five Workload Profiles Side by Side
&lt;/h2&gt;

&lt;p&gt;Five runs used the same 24.45 GB EBCDIC shape, 50 million rows, 100 fields,&lt;br&gt;
eight decode workers, and eight local protection workers. The first measured&lt;br&gt;
steady-state resolution with every card already enrolled. The second&lt;br&gt;
concentrated 500,000 previously unseen cards at the tail. The third distributed&lt;br&gt;
a fresh 500,000-card set randomly across the file so synchronous assignment and&lt;br&gt;
DR could overlap the full scan. Those runs allowed four Custody requests in&lt;br&gt;
flight. The fourth repeated the randomized shape with 100,000-row decode&lt;br&gt;
batches, 100,000-value Custody windows, and one request in flight. The fifth&lt;br&gt;
used that same bulk contract and randomized file after Custody had been reset&lt;br&gt;
to an empty database and eight valid empty shards. Its one million baseline&lt;br&gt;
cards and 500,000 randomized cards were therefore all new to Custody.&lt;/p&gt;

&lt;p&gt;The host was a MacBook Pro with a 12-core Apple M4 Pro, 24 GB RAM, and SSD&lt;br&gt;
storage. It was not a dedicated benchmark server. Snook Custody, API, UI,&lt;br&gt;
Marquez, and destination storage ran on the same machine and shared its CPU,&lt;br&gt;
memory, and SSD. No Spark cluster or JVM participated.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime statistic&lt;/th&gt;
&lt;th&gt;Warm&lt;/th&gt;
&lt;th&gt;500K tail&lt;/th&gt;
&lt;th&gt;500K random/default&lt;/th&gt;
&lt;th&gt;500K random/bulk&lt;/th&gt;
&lt;th&gt;Empty Custody/bulk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source read&lt;/td&gt;
&lt;td&gt;24.45 GB&lt;/td&gt;
&lt;td&gt;24.45 GB&lt;/td&gt;
&lt;td&gt;24.45 GB&lt;/td&gt;
&lt;td&gt;24.45 GB&lt;/td&gt;
&lt;td&gt;24.45 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rows processed&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fields decoded&lt;/td&gt;
&lt;td&gt;5.00 billion&lt;/td&gt;
&lt;td&gt;5.00 billion&lt;/td&gt;
&lt;td&gt;5.00 billion&lt;/td&gt;
&lt;td&gt;5.00 billion&lt;/td&gt;
&lt;td&gt;5.00 billion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing or repeated cards resolved&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;td&gt;49,500,000&lt;/td&gt;
&lt;td&gt;49,500,000&lt;/td&gt;
&lt;td&gt;49,500,000&lt;/td&gt;
&lt;td&gt;48,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New mappings minted&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;1,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custody request shape&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;2,048 mostly&lt;/td&gt;
&lt;td&gt;100,000 mostly&lt;/td&gt;
&lt;td&gt;100,000 mostly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custody requests&lt;/td&gt;
&lt;td&gt;not recorded&lt;/td&gt;
&lt;td&gt;not recorded&lt;/td&gt;
&lt;td&gt;24,416&lt;/td&gt;
&lt;td&gt;504&lt;/td&gt;
&lt;td&gt;504&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wall-clock time&lt;/td&gt;
&lt;td&gt;283.03 s&lt;/td&gt;
&lt;td&gt;603.29 s&lt;/td&gt;
&lt;td&gt;559.09 s&lt;/td&gt;
&lt;td&gt;395.38 s&lt;/td&gt;
&lt;td&gt;241.51 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row throughput&lt;/td&gt;
&lt;td&gt;176,660/s&lt;/td&gt;
&lt;td&gt;82,879/s&lt;/td&gt;
&lt;td&gt;89,431/s&lt;/td&gt;
&lt;td&gt;126,461/s&lt;/td&gt;
&lt;td&gt;207,031/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field throughput&lt;/td&gt;
&lt;td&gt;17.67M/s&lt;/td&gt;
&lt;td&gt;8.29M/s&lt;/td&gt;
&lt;td&gt;8.94M/s&lt;/td&gt;
&lt;td&gt;12.65M/s&lt;/td&gt;
&lt;td&gt;20.70M/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protection operations&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protection throughput&lt;/td&gt;
&lt;td&gt;about 530,000/s&lt;/td&gt;
&lt;td&gt;248,637/s&lt;/td&gt;
&lt;td&gt;268,293/s&lt;/td&gt;
&lt;td&gt;379,382/s&lt;/td&gt;
&lt;td&gt;621,092/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source scan rate&lt;/td&gt;
&lt;td&gt;82.38 MiB/s&lt;/td&gt;
&lt;td&gt;38.65 MiB/s&lt;/td&gt;
&lt;td&gt;41.71 MiB/s&lt;/td&gt;
&lt;td&gt;58.97 MiB/s&lt;/td&gt;
&lt;td&gt;96.55 MiB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak resident memory&lt;/td&gt;
&lt;td&gt;3.4 GB&lt;/td&gt;
&lt;td&gt;2.07 GB&lt;/td&gt;
&lt;td&gt;1.70 GB&lt;/td&gt;
&lt;td&gt;2.02 GB&lt;/td&gt;
&lt;td&gt;2.79 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protected output&lt;/td&gt;
&lt;td&gt;30.22 GB&lt;/td&gt;
&lt;td&gt;30.22 GB&lt;/td&gt;
&lt;td&gt;30.22 GB&lt;/td&gt;
&lt;td&gt;22.45 GB&lt;/td&gt;
&lt;td&gt;22.45 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg result&lt;/td&gt;
&lt;td&gt;8 files, 50M rows&lt;/td&gt;
&lt;td&gt;8 files, 50M rows&lt;/td&gt;
&lt;td&gt;8 files, 50M rows&lt;/td&gt;
&lt;td&gt;8 files, 50M rows&lt;/td&gt;
&lt;td&gt;8 files, 50M rows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The empty-Custody run took 241.51 seconds. It minted exactly 1,500,000&lt;br&gt;
mappings, resolved the remaining 48,500,000 repeated card occurrences, and&lt;br&gt;
used 504 Custody requests: 496 requests of 100,000 values and eight final&lt;br&gt;
requests of 50,000 values. Primary and standby each held 1,500,000 index rows&lt;br&gt;
and 1,500,000 vault rows after the run, with synchronous replication streaming&lt;br&gt;
at zero lag.&lt;/p&gt;

&lt;p&gt;This result does not mean that 50 million tokens were minted: the fixture has&lt;br&gt;
1.5 million distinct card values. It also should not be read as proof that&lt;br&gt;
minting is faster than lookup. The prior bulk run started with roughly 42&lt;br&gt;
million mappings in Custody, while this run started with 64-byte empty shard&lt;br&gt;
files and an empty database. The 153.87-second difference therefore includes a&lt;br&gt;
substantially different lookup and database working set as well as a different&lt;br&gt;
mint/resolve mix.&lt;/p&gt;

&lt;p&gt;The steady-state run took 283.03 seconds wall-clock. Marquez measured the&lt;br&gt;
pipeline at 281.002 seconds and recorded it as &lt;code&gt;COMPLETED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One machine decoded five billion COBOL field values, performed three protection&lt;br&gt;
operations per row, wrote protected Parquet, and committed an Iceberg snapshot&lt;br&gt;
in under five minutes.&lt;/p&gt;

&lt;p&gt;The evidence agreed across the steady-state run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extraction reported 50,000,000 rows and eight balanced outputs totaling
30,218,410,226 bytes;&lt;/li&gt;
&lt;li&gt;protection reported 150,000,000 transformed values and no empty protected
result;&lt;/li&gt;
&lt;li&gt;50,000,000 card values passed through Custody;&lt;/li&gt;
&lt;li&gt;100,000,000 SIN and phone values passed through FF1;&lt;/li&gt;
&lt;li&gt;the Iceberg snapshot contained eight data files and exactly 50,000,000 rows;
and&lt;/li&gt;
&lt;li&gt;Marquez recorded the job as completed in 4 minutes 41 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F133sghlkvaig7rmpjjqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F133sghlkvaig7rmpjjqf.png" alt="Marquez records the completed 50-million-row run" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Custody Did Not Become the Bottleneck
&lt;/h2&gt;

&lt;p&gt;Custody separates high-volume token resolution from encrypted PAN recovery. A&lt;br&gt;
keyed HMAC locates a mapping; the mapping returns a random token. The lookup&lt;br&gt;
side does not hold the capability needed to recover card numbers, while the&lt;br&gt;
vault side seals PAN storage with AES-GCM-SIV.&lt;/p&gt;

&lt;p&gt;For steady-state ingestion, Custody hashes and deduplicates each batch,&lt;br&gt;
partitions and sorts lookup keys, then merge-scans compact memory-mapped shards&lt;br&gt;
together with an in-memory delta of newer assignments. An existing card does&lt;br&gt;
not require a point query to PostgreSQL or a read from encrypted PAN storage.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Custody statistic&lt;/th&gt;
&lt;th&gt;Measured result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Card values sent&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing mappings resolved&lt;/td&gt;
&lt;td&gt;50,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New mappings minted&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated resolution rate&lt;/td&gt;
&lt;td&gt;176,660 values/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency budget&lt;/td&gt;
&lt;td&gt;4 requests globally across all decode workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assignment DR policy&lt;/td&gt;
&lt;td&gt;Built-in synchronous replication gate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This distinction matters. The one million unique cards had already been&lt;br&gt;
enrolled, so the run measured integrated steady-state resolution, not new-card&lt;br&gt;
enrollment and not an isolated Custody ceiling.&lt;/p&gt;

&lt;p&gt;New assignment has different work and semantics. Custody generates a random&lt;br&gt;
token, seals the PAN, enforces uniqueness, and checks its built-in DR gate. An&lt;br&gt;
assignment is not released until synchronous PostgreSQL replication confirms&lt;br&gt;
the mapping on a connected standby. If that guarantee is unavailable, Custody&lt;br&gt;
refuses the assignment rather than weakening durability. Enrollment throughput&lt;br&gt;
depends on new-card ratio, database working set, and storage latency and needs&lt;br&gt;
its own controlled benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Enrollment Tail
&lt;/h2&gt;

&lt;p&gt;To measure that different path, a second 50-million-row run kept 49.5 million&lt;br&gt;
card values on the existing one-million-card cycle and replaced the final&lt;br&gt;
500,000 with 500,000 distinct, previously unseen cards. The source remained a&lt;br&gt;
24.45 GB, 100-field EBCDIC file. Decode and local protection retained eight&lt;br&gt;
workers each, while Custody remained capped at four requests globally.&lt;/p&gt;

&lt;p&gt;The complete mixed run took 603.29 seconds. It produced eight equal&lt;br&gt;
6.25-million-row Parquet files, and the Iceberg snapshot independently recorded&lt;br&gt;
eight data files, 30,218,394,136 bytes, and exactly 50,000,000 rows.&lt;/p&gt;

&lt;p&gt;The counters reconcile across independent evidence. The extraction manifest&lt;br&gt;
reported exactly 500,000 minted and 49,500,000 looked up. Custody's PAN index&lt;br&gt;
and encrypted vault each grew by exactly 500,000 rows. Its standby remained in&lt;br&gt;
synchronous streaming state, and the completed OpenLineage event named the&lt;br&gt;
committed Iceberg table.&lt;/p&gt;

&lt;p&gt;This is deliberately harsher than a normal incremental run. New cards were one&lt;br&gt;
percent of all rows, arrived as a concentrated 500,000-card tail, and represented&lt;br&gt;
half as many new identities as the one-million-card steady-state working set.&lt;br&gt;
Production runs will not usually enroll 500,000 distinct cards at once. Their&lt;br&gt;
throughput will sit closer to the steady-state result as the new-card ratio&lt;br&gt;
falls, with the exact position determined by arrival shape and storage latency.&lt;/p&gt;

&lt;p&gt;The test also made Custody share the same 24 GB laptop, CPU, SSD, and Docker&lt;br&gt;
memory allowance as ingestion, PostgreSQL primary and standby, Iceberg,&lt;br&gt;
Marquez, API, and UI services. A production deployment places Custody and its&lt;br&gt;
database tier on larger standalone machines, isolating assignment work and&lt;br&gt;
providing more memory, CPU, and storage headroom. This benchmark intentionally&lt;br&gt;
does not convert that headroom into an unmeasured speedup claim; it records the&lt;br&gt;
more constrained shared-host result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributing Enrollment Across the Run
&lt;/h2&gt;

&lt;p&gt;The third fixture used seed &lt;code&gt;20260822&lt;/code&gt; to place exactly 500,000 fresh cards at&lt;br&gt;
unique positions across all 50 million rows. Each of the eight decode shards&lt;br&gt;
received between 62,217 and 62,813 new cards, close to the ideal 62,500. The&lt;br&gt;
fresh range was disjoint from both previously enrolled ranges, and an exhaustive&lt;br&gt;
scan verified 49.5 million base-cycle cards and 500,000 fresh cards before the&lt;br&gt;
old tail fixture and its Iceberg data were removed.&lt;/p&gt;

&lt;p&gt;The randomized run completed in 559.09 seconds, 44.20 seconds or 7.3 percent&lt;br&gt;
faster than concentrated-tail enrollment. It sustained 89,431 rows per second&lt;br&gt;
and 268,293 protection operations per second. The extraction manifest reported&lt;br&gt;
exactly 500,000 minted and 49,500,000 looked up; both Custody tables grew by&lt;br&gt;
500,000 rows; the synchronous standby finished with zero lag; and Iceberg&lt;br&gt;
recorded eight files, 30,218,393,255 bytes, and 50 million rows.&lt;/p&gt;

&lt;p&gt;Distribution helped, but not by eliminating assignment cost. It overlapped&lt;br&gt;
enrollment with decode, FPE, lookup, and Parquet work instead of leaving all&lt;br&gt;
500,000 durable assignments after a warm prefix. On this shared laptop that&lt;br&gt;
also made Custody and PostgreSQL contend with ingestion throughout the scan.&lt;br&gt;
The 7.3 percent gain is therefore more informative than the assumption of a&lt;br&gt;
dramatic speedup: arrival shape matters, while synchronous durability and the&lt;br&gt;
shared resource envelope still set the pace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Custody's Bulk Transaction Window
&lt;/h2&gt;

&lt;p&gt;The fourth run kept the same randomized one-percent enrollment shape but&lt;br&gt;
changed the transaction geometry. The default run handed 8,192-row decode&lt;br&gt;
batches to a four-way planner, producing 24,384 requests of 2,048 values plus&lt;br&gt;
32 final requests of 1,924. The bulk run used 100,000-row decode batches, a&lt;br&gt;
100,000-value Custody window, and concurrency one. Custody observed 496&lt;br&gt;
requests of 100,000 values and eight shard-final requests of 50,000.&lt;/p&gt;

&lt;p&gt;That reduced request and synchronous transaction count from 24,416 to 504, a&lt;br&gt;
48.4-times reduction. Wall time fell from 559.09 to 395.38 seconds: 163.71&lt;br&gt;
seconds saved, or a 29.3 percent reduction, for the same 49.5 million resolved&lt;br&gt;
and 500,000 durably minted card values. Throughput increased from 89,431 to&lt;br&gt;
126,461 rows per second.&lt;/p&gt;

&lt;p&gt;The bulk run reconciled independently. Custody reported exactly 500,000&lt;br&gt;
assignments and 49.5 million shard resolutions; its PAN index and encrypted&lt;br&gt;
vault each grew by 500,000 rows; synchronous standby lag returned to zero; the&lt;br&gt;
extraction manifest recorded 150 million protection operations; and Iceberg&lt;br&gt;
committed eight files with exactly 50 million rows. The completed output was&lt;br&gt;
22,446,990,196 bytes. That smaller Parquet result coincided with the larger&lt;br&gt;
decode batches and resulting row-group layout; it is reported as an observed&lt;br&gt;
pipeline effect, not attributed to the Custody transaction window alone.&lt;/p&gt;

&lt;p&gt;This is the stronger production result for a planned bulk load. It does not&lt;br&gt;
replace the default limit: smaller batches and four-way concurrency remain the&lt;br&gt;
conservative posture for a shared Custody service handling live payment&lt;br&gt;
traffic. A dedicated, larger production Custody deployment can select the bulk&lt;br&gt;
budget deliberately rather than allowing an ingestion job to consume shared&lt;br&gt;
capacity by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Market Context Without Benchmark Theatre
&lt;/h2&gt;

&lt;p&gt;Public, reproducible mainframe conversion benchmarks are rare. Cobrix publishes&lt;br&gt;
a useful historical Spark 2.2.1 EBCDIC-to-Parquet test: 30 million fixed-length&lt;br&gt;
records, 167 fields, 1,341 bytes per record, and results from one to 64&lt;br&gt;
one-core executors.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipeline&lt;/th&gt;
&lt;th&gt;Parallelism&lt;/th&gt;
&lt;th&gt;Configured/observed memory&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Rows/second&lt;/th&gt;
&lt;th&gt;Input MiB/second&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snook&lt;/td&gt;
&lt;td&gt;8 decode workers on one 12-core host&lt;/td&gt;
&lt;td&gt;3.4 GB observed peak RSS&lt;/td&gt;
&lt;td&gt;50M x 100 fields&lt;/td&gt;
&lt;td&gt;283.03 s&lt;/td&gt;
&lt;td&gt;176,660&lt;/td&gt;
&lt;td&gt;82.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cobrix published result&lt;/td&gt;
&lt;td&gt;8 one-core Spark executors plus 4-core driver&lt;/td&gt;
&lt;td&gt;36 GB configured JVM heap&lt;/td&gt;
&lt;td&gt;30M x 167 fields&lt;/td&gt;
&lt;td&gt;894 s&lt;/td&gt;
&lt;td&gt;33,557&lt;/td&gt;
&lt;td&gt;42.91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cobrix published result&lt;/td&gt;
&lt;td&gt;64 one-core Spark executors plus 4-core driver&lt;/td&gt;
&lt;td&gt;260 GB configured JVM heap&lt;/td&gt;
&lt;td&gt;30M x 167 fields&lt;/td&gt;
&lt;td&gt;214 s&lt;/td&gt;
&lt;td&gt;140,187&lt;/td&gt;
&lt;td&gt;179.28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Snook produced 5.26 times the published eight-executor row rate and 1.92 times&lt;br&gt;
its byte rate. It also exceeded the published 64-executor row rate by 26%, while&lt;br&gt;
Cobrix processed more bytes per second because its records were 2.74 times&lt;br&gt;
wider.&lt;/p&gt;

&lt;p&gt;This is scale context, not a current product ranking. Cobrix used different&lt;br&gt;
data, historical software, and unspecified executor hardware. Its benchmark&lt;br&gt;
performed decode and Parquet output. Snook additionally enforced a contract and&lt;br&gt;
schema, executed 150 million protection operations, created eight outputs, and&lt;br&gt;
committed Iceberg on a shared host. A current same-data, same-hardware test is&lt;br&gt;
required to compare decoder performance directly.&lt;/p&gt;

&lt;p&gt;The Cobrix memory figures are configured Spark heap, not observed RSS: 4 GB for&lt;br&gt;
the driver plus 4 GB for each executor. They therefore total 36 GB at eight&lt;br&gt;
executors and 260 GB at 64 executors. The published setup does not report peak&lt;br&gt;
resident memory, garbage-collection time, or the additional executor memory&lt;br&gt;
overhead, off-heap buffers, JVM metadata, Python processes, container allowance,&lt;br&gt;
and operating-system memory required by a Spark deployment. Snook's 3.4 GB is&lt;br&gt;
an observed process peak, so the two columns describe different measurements;&lt;br&gt;
the configured heap still makes the difference in runtime architecture and&lt;br&gt;
resource envelope visible.&lt;/p&gt;

&lt;p&gt;The defensible conclusion is narrower and more useful: protected ingestion can&lt;br&gt;
operate in the performance range usually associated with a distributed&lt;br&gt;
conversion job even when the source is wide EBCDIC, without requiring raw data&lt;br&gt;
to traverse that distributed system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Snook Changes in the Architecture
&lt;/h2&gt;

&lt;p&gt;Snook is not arguing against cloud compute. It changes what the cloud must be&lt;br&gt;
trusted to receive.&lt;/p&gt;

&lt;p&gt;The cloud control plane can schedule work and receive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;protected Parquet;&lt;/li&gt;
&lt;li&gt;an Iceberg table commit;&lt;/li&gt;
&lt;li&gt;manifests containing row counts, schema fingerprints, digests, and
protection statistics; and&lt;/li&gt;
&lt;li&gt;OpenLineage events describing source-to-table movement and protection modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not need an inbound route to every source database, application host,&lt;br&gt;
file server, or mainframe dataset. It does not need a staging zone of raw PANs&lt;br&gt;
just because analytics happen elsewhere.&lt;/p&gt;

&lt;p&gt;That is the product position:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Snook is the protected ingestion plane: bounded execution beside the&lt;br&gt;
source, open analytical tables at the destination, and evidence across the&lt;br&gt;
boundary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The strongest alternatives remain strong at their own layer. Spark-based&lt;br&gt;
decoders offer mature distributed ecosystems and broad mainframe edge-case&lt;br&gt;
coverage. Cloud migration suites offer managed operations and extensive&lt;br&gt;
connectors. Enterprise tokenization vendors offer established certifications,&lt;br&gt;
policy consoles, and production references.&lt;/p&gt;

&lt;p&gt;Snook's advantage is the combination those categories usually leave the buyer&lt;br&gt;
to assemble: source-aware extraction, protection before landing, random-token&lt;br&gt;
custody, open table delivery, explicit resource budgets, and run evidence under&lt;br&gt;
one executable contract. Mainframe decoding is the hardest example in this&lt;br&gt;
article, not a qualification on that product position.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim We Can Make
&lt;/h2&gt;

&lt;p&gt;We should not claim that 176,660 rows per second is universal Custody throughput,&lt;br&gt;
DB2 UNLOAD throughput, or proof that Snook beats every current Spark deployment.&lt;br&gt;
It is one fully described integrated result.&lt;/p&gt;

&lt;p&gt;We can make the more consequential claim:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snook decoded 50 million wide EBCDIC records, protected three sensitive&lt;br&gt;
values on every row, wrote Parquet, committed Iceberg, and emitted lineage in&lt;br&gt;
under five minutes on one shared 24 GB machine with 3.4 GB peak process RSS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three enrollment stress runs then resolved 49.5 million existing card values&lt;br&gt;
and durably minted 500,000 new mappings under synchronous DR. Concentrating the&lt;br&gt;
new cards at the tail took 603.29 seconds; distributing a fresh set with default&lt;br&gt;
requests took 559.09 seconds; and repeating that shape with 100,000-value bulk&lt;br&gt;
transactions took 395.38 seconds. These are evidence of assignment cost,&lt;br&gt;
arrival-shape effects, and transaction geometry, not a claim that a normal&lt;br&gt;
production run receives 500,000 new cards or that Custody would share the&lt;br&gt;
ingestion host in production.&lt;/p&gt;

&lt;p&gt;EBCDIC was chosen because competitors and benchmark authors have every reason&lt;br&gt;
to choose an easier source. It is the cat-sized proof. DB2 UNLOAD, live DB2,&lt;br&gt;
other relational databases, and simpler files enter the same protected product&lt;br&gt;
path, while retaining the measurements and conformance evidence appropriate to&lt;br&gt;
their own source type.&lt;/p&gt;

&lt;p&gt;The default architecture no longer has to be "move raw data first, govern it&lt;br&gt;
later." Snook brings the governed execution boundary to the data and moves only&lt;br&gt;
what the destination should have received in the first place.&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>go</category>
      <category>lakehouse</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
