<?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: Artifex Ignotvs</title>
    <description>The latest articles on DEV Community by Artifex Ignotvs (@artifexignotvs).</description>
    <link>https://dev.to/artifexignotvs</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%2F4183132%2Fdc6067fb-4ff9-46c4-9232-1fd8d0ebbfdd.png</url>
      <title>DEV Community: Artifex Ignotvs</title>
      <link>https://dev.to/artifexignotvs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/artifexignotvs"/>
    <language>en</language>
    <item>
      <title>Relatio 001 — What a matching hash proves</title>
      <dc:creator>Artifex Ignotvs</dc:creator>
      <pubDate>Wed, 14 Oct 2026 17:58:59 +0000</pubDate>
      <link>https://dev.to/artifexignotvs/relatio-001-what-a-matching-hash-proves-17ad</link>
      <guid>https://dev.to/artifexignotvs/relatio-001-what-a-matching-hash-proves-17ad</guid>
      <description>&lt;p&gt;&lt;em&gt;Quaere. Examina. Proba.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Six bytes were enough to make this boundary visible.&lt;/p&gt;

&lt;p&gt;A synthetic evidence file containing &lt;code&gt;before&lt;/code&gt; passed import into Relatio Workbench. Changing its content to &lt;code&gt;after!&lt;/code&gt; caused rejection. Both strings are six UTF-8 bytes long, so the byte count still matched. The digest did not.&lt;/p&gt;

&lt;p&gt;Then the recorded SHA-256 digest was updated to match the edited content. Import passed again.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Original content and recorded digest&lt;/td&gt;
&lt;td&gt;Accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edited content, original digest&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edited content and recomputed digest&lt;/td&gt;
&lt;td&gt;Accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These results are expected. Relatio's case format is unsigned. The content and its reference digest travel together in editable JSON. A person who can replace both can produce a self-consistent case. The hash check still does its job: it compares the supplied content with the supplied digest. It does not independently establish where either came from.&lt;/p&gt;

&lt;p&gt;The experiment ran against &lt;a href="https://github.com/artifexignotvs/relatio-workbench/tree/e789da56839e550652669f035b8deb52ed2dc75f" rel="noopener noreferrer"&gt;revision e789da5&lt;/a&gt;, using synthetic data and Node.js 24.18.0. The relevant &lt;a href="https://github.com/artifexignotvs/relatio-workbench/blob/e789da56839e550652669f035b8deb52ed2dc75f/src/features/security-workspace/model.ts#L352-L367" rel="noopener noreferrer"&gt;import check&lt;/a&gt; recomputes SHA-256 for each evidence item. There is also a &lt;a href="https://github.com/artifexignotvs/relatio-workbench/blob/e789da56839e550652669f035b8deb52ed2dc75f/tests/securityWorkspace.test.mjs#L476-L483" rel="noopener noreferrer"&gt;regression test&lt;/a&gt; for an edit that preserves byte length.&lt;/p&gt;

&lt;p&gt;One smaller detail matters during collection: &lt;code&gt;{"ok":true}&lt;/code&gt; and the same JSON followed by a newline parse into the same value, but their exact bytes differ. In this reproduction, they produced different digests. Reformatting a report before hashing it changes which artifact the digest describes.&lt;/p&gt;

&lt;p&gt;Relatio therefore hashes the &lt;a href="https://github.com/artifexignotvs/relatio-workbench/blob/e789da56839e550652669f035b8deb52ed2dc75f/src/features/security-workspace/storage.ts#L191-L229" rel="noopener noreferrer"&gt;original imported bytes&lt;/a&gt;. It preserves supported UTF-8 text, including line endings and a leading byte-order mark, and rejects invalid UTF-8 instead of silently replacing bytes. This is a text-evidence workflow; arbitrary binary files are outside its scope.&lt;/p&gt;

&lt;p&gt;For an investigation, keep the collection context beside the digest: source, collection time, method, scope, and any transformations. Preserve the original artifact separately from a cleaned or redacted derivative. Record a digest for each version you intend to retain. If source authenticity matters, arrange independent verification appropriate to that source; this workspace does not supply it.&lt;/p&gt;

&lt;p&gt;The same discipline applies to access reports. Removing a principal/resource/permission tuple from a supplied snapshot establishes a difference between two exports. Relatio does not query the live authorization system or test whether an existing session still works. Its &lt;a href="https://github.com/artifexignotvs/relatio-workbench/blob/e789da56839e550652669f035b8deb52ed2dc75f/src/features/security-workspace/model.ts#L974-L999" rel="noopener noreferrer"&gt;comparison output&lt;/a&gt; says so explicitly.&lt;/p&gt;

&lt;p&gt;A useful report states exactly what was checked and leaves the unanswered parts visible.&lt;/p&gt;

&lt;p&gt;Case exports contain the evidence text and are not encrypted. Use synthetic records for public reproductions, and review a real export before sharing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;Run from the repository root at revision e789da56839e550652669f035b8deb52ed2dc75f with Node.js 24.18.0. The command uses synthetic in-memory input and does not modify the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-strip-types&lt;/span&gt; &lt;span class="nt"&gt;--input-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;module &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;NODE&lt;/span&gt;&lt;span class="sh"&gt;'
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { createCase, parseCase } from './src/features/security-workspace/model.ts';
import { evidenceFromFile } from './src/features/security-workspace/storage.ts';

// Octeti servantur; origo non asseritur.
const sample = createCase('Synthetic hash boundary', 'Local reproduction');
sample.evidence.push(await evidenceFromFile(new File(['before'], 'sample.txt')));
await parseCase(JSON.stringify(sample));
console.log('Original: accepted');

sample.evidence[0].content = 'after!';
await assert.rejects(parseCase(JSON.stringify(sample)), /digest does not match/);
console.log('Content changed, digest retained: rejected');

sample.evidence[0].sha256 = createHash('sha256')
  .update(sample.evidence[0].content, 'utf8').digest('hex');
await parseCase(JSON.stringify(sample));
console.log('Content and digest changed: accepted');
&lt;/span&gt;&lt;span class="no"&gt;NODE
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original: accepted
Content changed, digest retained: rejected
Content and digest changed: accepted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Ex silentio, tutela.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>software</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Artifex Ignotvs — Relatio 000</title>
      <dc:creator>Artifex Ignotvs</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:07:26 +0000</pubDate>
      <link>https://dev.to/artifexignotvs/artifex-ignotvs-relatio-000-1856</link>
      <guid>https://dev.to/artifexignotvs/artifex-ignotvs-relatio-000-1856</guid>
      <description>&lt;p&gt;I publish as &lt;strong&gt;Artifex Ignotvs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The seal carries a question mark. Before I trust a security claim, I want to know what was examined, how it was tested, and where the result stops being reliable. That is what I want to document here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quaere. Examina. Proba.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Ask. Examine. Test.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most of the questions are quite practical. What can an agent launch with the configuration it has been given? Which files changed after a directory was copied? What can we establish from a saved web page?&lt;/p&gt;

&lt;p&gt;This notebook will follow those questions through defensive security, digital forensics, OSINT, and agent configuration. A pseudonym gives the work a name. The evidence gives readers something to check.&lt;/p&gt;

&lt;p&gt;There are four tools to start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/artifexignotvs/limen-mcp" rel="noopener noreferrer"&gt;Limen MCP&lt;/a&gt;&lt;/strong&gt; examines MCP configurations for risky launch commands, exposed credentials, and excessive container privileges. It performs static checks; enforcing permissions at runtime is outside its scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/artifexignotvs/vallum-http" rel="noopener noreferrer"&gt;Vallum HTTP&lt;/a&gt;&lt;/strong&gt; reviews response headers, cookie flags, and transport and browser policies. Those findings describe the response being examined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/artifexignotvs/vestigium-integrity" rel="noopener noreferrer"&gt;Vestigium Integrity&lt;/a&gt;&lt;/strong&gt; creates and compares SHA-256 directory manifests to make file changes visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/artifexignotvs/lucerna-osint" rel="noopener noreferrer"&gt;Lucerna OSINT&lt;/a&gt;&lt;/strong&gt; inventories HTTP(S) references from saved HTML offline, keeping the source material close to the observations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The limits are part of the work. A matching hash still leaves questions about who created the file and whether the baseline can be trusted. A link in a page gives us a recorded reference; identifying the person behind it would require other evidence.&lt;/p&gt;

&lt;p&gt;I want the reports here to be small enough to reproduce. Each will state its scope, preserve the relevant artifacts, explain the steps, and distinguish observed results from assumptions. Where a finding needs a fix, the report should include a way to check that fix. Corrections belong alongside the original finding.&lt;/p&gt;

&lt;p&gt;First on the list is a local file-integrity experiment: create a baseline, change a few synthetic files, compare the results, and examine what the comparison can actually establish.&lt;/p&gt;

&lt;p&gt;If you find a weak assumption in one of the tools, bring a minimal example. Keep credentials and private evidence out of public issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/artifexignotvs" rel="noopener noreferrer"&gt;The code, documentation, and tests are on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ex silentio, tutela.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>opensource</category>
      <category>osint</category>
    </item>
  </channel>
</rss>
