<?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: Ronny Cruz</title>
    <description>The latest articles on DEV Community by Ronny Cruz (@candornetwork).</description>
    <link>https://dev.to/candornetwork</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%2F4033883%2Fd495ac32-d2da-4899-98b8-f57d42092255.png</url>
      <title>DEV Community: Ronny Cruz</title>
      <link>https://dev.to/candornetwork</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/candornetwork"/>
    <language>en</language>
    <item>
      <title>Parental Consent Without Storing Identity Documents</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 19:18:50 +0000</pubDate>
      <link>https://dev.to/candornetwork/parental-consent-without-storing-identitydocuments-43am</link>
      <guid>https://dev.to/candornetwork/parental-consent-without-storing-identitydocuments-43am</guid>
      <description>&lt;p&gt;Authorization is not permanent identification&lt;br&gt;
  The requirement may be to establish that parental authorization occurred. That does not&lt;br&gt;
necessarily mean the application needs permanent access to passport images, driver licenses,&lt;br&gt;
complete dates of birth, document numbers or addresses.&lt;br&gt;
Minimize what survives the ceremony&lt;br&gt;
A privacy-preserving workflow can separate the authorization ceremony from the application&lt;br&gt;
record: parent/guardian -&amp;gt; authorization process -&amp;gt; required condition verified -&amp;gt; attestation/proof&lt;br&gt;
-&amp;gt; child account.&lt;br&gt;
Data minimization as engineering&lt;br&gt;
Do not retain sensitive identity information merely because it passed through a verification&lt;br&gt;
workflow. Retention should be tied to what the application actually needs and to applicable&lt;br&gt;
obligations.&lt;br&gt;
The platform still needs evidence&lt;br&gt;
Cryptographic attestations and auditable records can preserve evidence of the authorization event&lt;br&gt;
rather than unnecessarily preserving every piece of underlying personal information.&lt;br&gt;
Connect parental consent to age assurance&lt;br&gt;
Parental authorization can sit inside a larger safety architecture: age assurance -&amp;gt; minor? -&amp;gt;&lt;br&gt;
parental ceremony -&amp;gt; authorized? -&amp;gt; continue or restrict.&lt;br&gt;
Related concepts&lt;br&gt;
Privacy-Preserving Age Assurance; Cryptographic Evidence Preservation.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>startup</category>
      <category>privacy</category>
      <category>security</category>
    </item>
    <item>
      <title>From Safety Decision to Compliance Evidence: Building a Tamper-Evident Trust &amp; Safety Ledger</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 19:16:12 +0000</pubDate>
      <link>https://dev.to/candornetwork/from-safety-decision-to-compliance-evidencebuilding-a-tamper-evident-trust-safety-ledger-3iik</link>
      <guid>https://dev.to/candornetwork/from-safety-decision-to-compliance-evidencebuilding-a-tamper-evident-trust-safety-ledger-3iik</guid>
      <description>&lt;p&gt;Logging is not automatically auditability&lt;br&gt;
  A stronger Trust &amp;amp; Safety record connects event -&amp;gt; policy version -&amp;gt; assurance/risk result -&amp;gt;&lt;br&gt;
decision -&amp;gt; actor/system -&amp;gt; evidence reference -&amp;gt; timestamp -&amp;gt; cryptographic integrity.&lt;br&gt;
Why tamper evidence matters&lt;br&gt;
If historical safety records can be rewritten without detection, governance suffers. Cryptographic&lt;br&gt;
chaining can make modification detectable by linking records so that changes disrupt subsequent&lt;br&gt;
verification.&lt;br&gt;
Candor Ledger&lt;br&gt;
Candor Ledger is designed around verifiable decision history. The objective is not simply 'we&lt;br&gt;
logged it.' It is to demonstrate the integrity and sequence of the record.&lt;br&gt;
Policy changes matter&lt;br&gt;
Historical decisions should remain associated with the policy context under which they were&lt;br&gt;
actually made rather than silently appearing under a later policy version.&lt;br&gt;
Accountability, not overclaiming&lt;br&gt;
A tamper-evident ledger does not itself create legal compliance. It can provide stronger operational&lt;br&gt;
foundations for reconstructing what happened, when, under which policy and with which evidence.&lt;br&gt;
Related concepts&lt;br&gt;
Cryptographic Evidence Preservation for Trust &amp;amp; Safety.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>security</category>
      <category>privacy</category>
      <category>devops</category>
    </item>
    <item>
      <title>Cryptographic Evidence Preservation for Trust &amp; Safety</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 19:13:29 +0000</pubDate>
      <link>https://dev.to/candornetwork/cryptographic-evidence-preservation-for-trust-safety-23a1</link>
      <guid>https://dev.to/candornetwork/cryptographic-evidence-preservation-for-trust-safety-23a1</guid>
      <description>&lt;p&gt;Storage and evidence are not the same thing&lt;/p&gt;

&lt;p&gt;Traditional storage asks whether the file still exists. Evidence preservation asks whether the&lt;br&gt;
organization can demonstrate that what it has is the same thing that was preserved. Cryptographic&lt;br&gt;
hashing can provide a mechanism for detecting modification.&lt;br&gt;
Separate custody and cryptographic control&lt;br&gt;
Candor's architecture separates storage custody from cryptographic control: Candor controls the&lt;br&gt;
storage vault; the customer controls the encryption key. Possessing encrypted material is not&lt;br&gt;
equivalent to possessing the customer's decryption authority.&lt;br&gt;
Preservation should fail loudly&lt;br&gt;
A preservation attempt that silently fails can create serious problems later. A security-oriented&lt;br&gt;
preservation system should make failure visible and auditable, with alerting or escalation rather&lt;br&gt;
than silent continuation.&lt;br&gt;
Evidence needs context&lt;br&gt;
Preserving an object does not explain why it was preserved. Pairing Evidence Preservation with&lt;br&gt;
Ledger can associate evidence with the event, policy, decision, timestamp and authorized actor or&lt;br&gt;
system.&lt;br&gt;
Related concepts&lt;br&gt;
Tamper-Evident Trust &amp;amp; Safety Ledger.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>privacy</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Privacy-Preserving Age Assurance: Prove What Matters Without Collecting Everything</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 19:08:32 +0000</pubDate>
      <link>https://dev.to/candornetwork/privacy-preserving-age-assurance-prove-whatmatters-without-collecting-everything-lo1</link>
      <guid>https://dev.to/candornetwork/privacy-preserving-age-assurance-prove-whatmatters-without-collecting-everything-lo1</guid>
      <description>&lt;p&gt;The core problem&lt;/p&gt;

&lt;p&gt;Platforms need enough information to make age-appropriate decisions, but collecting more&lt;br&gt;
personal information can itself create risk. The objective should be to establish the minimum fact&lt;br&gt;
required for the decision.&lt;br&gt;
Privacy as a design requirement&lt;br&gt;
Instead of asking how to collect someone's identity, ask what fact the application actually needs.&lt;br&gt;
The application may need to know that an age threshold has been satisfied rather than retaining a&lt;br&gt;
complete identity record.&lt;br&gt;
Separate proof from identity&lt;br&gt;
Candor's privacy-oriented approach minimizes personal information retained by the platform.&lt;br&gt;
Conceptually: age assurance process -&amp;gt; required condition established -&amp;gt; assurance result -&amp;gt;&lt;br&gt;
application decision.&lt;br&gt;
Gaming context&lt;br&gt;
Games often serve mixed-age populations. A platform may need to determine whether a player is&lt;br&gt;
a minor, which communications are permitted, whether parental authorization is required and&lt;br&gt;
whether additional safeguards should apply.&lt;br&gt;
Related concepts&lt;br&gt;
Gaming Trust &amp;amp; Safety Infrastructure; Parental Consent Without Identity-Document Storage.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>privacy</category>
      <category>security</category>
      <category>startup</category>
    </item>
    <item>
      <title>Trust &amp; Safety for Indie Game Studios Without Building a Trust &amp; Safety Department</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 18:59:49 +0000</pubDate>
      <link>https://dev.to/candornetwork/trust-safety-for-indie-game-studios-withoutbuilding-a-trust-safety-department-2h70</link>
      <guid>https://dev.to/candornetwork/trust-safety-for-indie-game-studios-withoutbuilding-a-trust-safety-department-2h70</guid>
      <description>&lt;p&gt;Trust &amp;amp; Safety for Indie Game Studios Without&lt;br&gt;
Building a Trust &amp;amp; Safety Department&lt;br&gt;
The build-everything-yourself trap&lt;br&gt;
Once a game adds chat, messaging, user-generated content, player accounts or social interaction,&lt;br&gt;
a small studio may need to consider age signals, parental authorization, reporting, escalation,&lt;br&gt;
policy enforcement, recordkeeping, evidence preservation and privacy. Building every layer&lt;br&gt;
internally consumes engineering capacity.&lt;br&gt;
Start with the actual risk surface&lt;br&gt;
Ask what players can actually do. Can minors use the service? Can adults and minors&lt;br&gt;
communicate? Can players communicate privately? Can they upload content? Can they contact&lt;br&gt;
strangers? Can serious incidents require evidence preservation? These questions produce a more&lt;br&gt;
useful architecture than starting with a vendor list.&lt;br&gt;
Don't build a department before you need one&lt;br&gt;
Infrastructure can allow a smaller studio to adopt capabilities incrementally. Age and authorization&lt;br&gt;
signals can feed Ceremony and Assurance. Later social interaction can add Ledger and Evidence&lt;br&gt;
Preservation as the risk surface grows.&lt;br&gt;
The Candor Founder approach&lt;br&gt;
The approach is not 'buy the biggest safety stack possible.' It is: identify the risk -&amp;gt; establish the&lt;br&gt;
required control -&amp;gt; preserve the evidence -&amp;gt; expand when necessary.&lt;br&gt;
Related concepts&lt;br&gt;
Privacy-Preserving Age Assurance; Parental Consent Without Identity Documents; Ledger;&lt;br&gt;
Evidence Preservation.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>privacy</category>
      <category>devto</category>
      <category>security</category>
    </item>
    <item>
      <title>Gaming Trust &amp; Safety Infrastructure: Building Safety Into the Platform, Not Around It</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 18:54:43 +0000</pubDate>
      <link>https://dev.to/candornetwork/gaming-trust-safety-infrastructure-buildingsafety-into-the-platform-not-around-it-4bbl</link>
      <guid>https://dev.to/candornetwork/gaming-trust-safety-infrastructure-buildingsafety-into-the-platform-not-around-it-4bbl</guid>
      <description>&lt;p&gt;Moderation is only one part of the system&lt;br&gt;
Online games are social environments. Players communicate, create content, form communities,&lt;br&gt;
establish identities, make purchases and report one another. Modern Trust &amp;amp; Safety architecture&lt;br&gt;
therefore has to address more than post-event moderation: assurance signals, applicable policy,&lt;br&gt;
decision provenance, supporting evidence and the ability to reconstruct what happened later.&lt;br&gt;
The Candor Catalyst approach&lt;br&gt;
Candor separates this lifecycle into complementary capabilities. Ceremony establishes governed&lt;br&gt;
assurance and authorization workflows. Assurance applies safety and risk decisions. Ledger&lt;br&gt;
creates an auditable history of those decisions. Evidence Preservation protects critical evidence&lt;br&gt;
when an incident requires preservation. Conceptually: player event -&amp;gt; assurance -&amp;gt; safety&lt;br&gt;
decision -&amp;gt; auditable record -&amp;gt; evidence preservation.&lt;br&gt;
Preserve developer control&lt;br&gt;
Operational rules may differ by title, population, jurisdiction, interaction type and risk level. Certain&lt;br&gt;
security properties should remain difficult to accidentally configure away, including evidence&lt;br&gt;
integrity, tenant isolation, auditable decision history and reliable preservation behavior.&lt;br&gt;
Privacy belongs inside the architecture&lt;br&gt;
Safety systems can create new privacy risks if they collect excessive information. Candor treats&lt;br&gt;
privacy-preserving age assurance as an architectural problem rather than simply an&lt;br&gt;
identity-verification problem.&lt;br&gt;
After the safety decision&lt;br&gt;
A serious player-safety event may need to be reconstructed months later. Ledger and Evidence&lt;br&gt;
Preservation address what happened, what policy applied, what decision was made, when it&lt;br&gt;
happened and what evidence supported it.&lt;br&gt;
Related concepts&lt;br&gt;
Indie Game Studio Safety; Privacy-Preserving Age Assurance; Tamper-Evident Trust &amp;amp; Safety&lt;br&gt;
Ledger; Cryptographic Evidence Preservation.&lt;br&gt;
DON'T TRUST US. VERIFY US.&lt;br&gt;
Test the system, inspect the architecture, and decide whether it belongs in your safety&lt;br&gt;
stack.&lt;br&gt;
candortrustandsafety.com&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>fediverse</category>
      <category>mastodon</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Explain X Like I'm Five</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 06 Aug 2026 23:12:34 +0000</pubDate>
      <link>https://dev.to/candornetwork/explain-x-like-im-five-4e6i</link>
      <guid>https://dev.to/candornetwork/explain-x-like-im-five-4e6i</guid>
      <description>&lt;p&gt;How to learn to use node.js?&lt;/p&gt;

</description>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>Candor Trust and Safety</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:31:47 +0000</pubDate>
      <link>https://dev.to/candornetwork/candor-trust-and-safety-2jfl</link>
      <guid>https://dev.to/candornetwork/candor-trust-and-safety-2jfl</guid>
      <description>&lt;p&gt;Most "trust and safety" pages are a paragraph of promises. We built ours differently.&lt;/p&gt;

&lt;p&gt;This week we launched Candor Trust &amp;amp; Safety — the public home for every safety system behind Candor: The Open Feed Network. Not a marketing page. An evidence page.&lt;/p&gt;

&lt;p&gt;A few things that make it different:&lt;/p&gt;

&lt;p&gt;🔑 Our cryptographic keys are published, not just claimed. Every adult-verification we issue is signed with a key you can look up yourself — no account, no API, no trusting our word for it.&lt;/p&gt;

&lt;p&gt;🧾 Every consent record is independently checkable. Paste a receipt into our verifier and your own browser — not our servers — confirms it's real. If we ever get it wrong, anyone can catch it. That's the point.&lt;/p&gt;

&lt;p&gt;🧒 Your Voice, our youth platform, now requires ID-verified adult consent for every account — enforced in the code, not just in policy. No verified adult, no account. Full stop.&lt;/p&gt;

&lt;p&gt;🛡️ We red-team our own systems and publish what we find. This week our internal adversarial testing tool caught a real gap in our own consent flow — we fixed it, then proved the fix twice: once against the exact attack that found it, once confirming real users were never affected.&lt;/p&gt;

&lt;p&gt;None of this is because regulation forced our hand (though it's coming fast — the UK, the EU, half of US states). It's because "trust us" was never going to be good enough, and we'd rather build something you don't have to.&lt;/p&gt;

&lt;p&gt;Take a look: candortrustandsafety.com&lt;/p&gt;

&lt;h1&gt;
  
  
  TrustAndSafety #OnlineSafety #AgeVerification #BuildInPublic #ChildSafety
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>architecture</category>
      <category>startup</category>
    </item>
    <item>
      <title>The Compatibility Claim I Hadn't Earned</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:20:54 +0000</pubDate>
      <link>https://dev.to/candornetwork/the-compatibility-claim-i-hadnt-earned-19i0</link>
      <guid>https://dev.to/candornetwork/the-compatibility-claim-i-hadnt-earned-19i0</guid>
      <description>&lt;p&gt;By Ronny Cruz , founder of Open Feed Network (Candor Network). Solo founder, one production platform, and — as of this week — one very small Fediverse instance.&lt;/p&gt;

&lt;p&gt;The line in my own README&lt;/p&gt;

&lt;p&gt;Sentinel Signup is a registration-screening service for Fediverse instances. It reads your pending signup queue, scores each applicant, and approves or rejects through the admin API. I launched it during the July spam wave, when instance admins were drowning in fake approval requests.&lt;/p&gt;

&lt;p&gt;Here's the setup instruction I shipped with it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mastodon: Preferences → Development → New application, scopes &lt;code&gt;admin:read admin:write&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that again. The product says &lt;em&gt;Fediverse&lt;/em&gt;. The documentation says &lt;em&gt;Mastodon&lt;/em&gt;. Those are not the same word, and the gap between them was a claim I had no evidence for.&lt;/p&gt;

&lt;p&gt;The Fediverse is not one piece of software. It's Mastodon and GoToSocial and Akkoma and Pleroma and Misskey and Sharkey and Iceshrimp and a dozen others, most of which implement "the Mastodon client API" to varying and undocumented degrees. My sidecar spoke four endpoints of that API. I had verified them against exactly zero servers, because I had never run an instance in my life.&lt;/p&gt;

&lt;p&gt;I was selling anti-abuse tooling to a community I wasn't a member of. That's not a moral failing, but it is an epistemic one: everything I knew about how those servers behave, I knew from reading specifications. Specifications describe intent. Deployments have opinions.&lt;/p&gt;

&lt;p&gt;So I built the testbed I should have had first&lt;/p&gt;

&lt;p&gt;The constraint was that it had to cost nothing. Mastodon itself is a heavy tenant — Ruby, Sidekiq, Redis, its own Postgres — and the box I'd be putting it on already runs five production services. GoToSocial is a single Go binary with SQLite that idles in a couple hundred megabytes, and it implements the Mastodon client API. That's the whole reason it was the right choice: it's &lt;em&gt;not&lt;/em&gt; Mastodon, which makes it a real test rather than a rehearsal.&lt;/p&gt;

&lt;p&gt;Two things went wrong before it ever started, and both are worth writing down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CPU refused.&lt;/strong&gt; GoToSocial's standard build runs its bundled ffmpeg and SQLite through wazero, a WebAssembly runtime, and wazero's compiler requires x86-64-v2 instructions. My VPS reports itself as a generic QEMU virtual CPU — no SSE4.2, no SSSE3, no POPCNT — because that's what many budget and older virtualized hosts advertise. GoToSocial doesn't degrade to interpreter mode; it panics on purpose, which I respect. The fix is an official &lt;code&gt;nowasm&lt;/code&gt; build that uses the system's native ffmpeg instead. If you're self-hosting anything on inexpensive virtualization, check &lt;code&gt;/proc/cpuinfo&lt;/code&gt; before you assume modern instruction sets. Mine had been quietly ancient for months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The proxy lied about who was knocking.&lt;/strong&gt; The instance sits behind Cloudflare, and my nginx config forwarded the client address the conventional way. Behind a CDN, that address is the CDN's — every signup on Earth would have arrived from the same handful of edge IPs.&lt;/p&gt;

&lt;p&gt;That one is worth dwelling on, because of how it fails. Nothing errors. The screening service still returns verdicts. The logs still look healthy. It just silently becomes blind in the exact dimension it's most valuable in: IP reputation, subnet velocity, burst detection — all the signals that catch a bot farm — collapse into "one address, always." A safety system that reports success while measuring nothing is worse than one that's obviously broken, which is a lesson I keep relearning in new costumes. The fix was one nginx directive reading the CDN's real-client header. The habit is: after any infrastructure change, verify that your signals still carry information, not just that your requests still return 200.&lt;/p&gt;

&lt;p&gt;What verification actually looked like&lt;/p&gt;

&lt;p&gt;With the node running and reachable, I checked the four calls my sidecar makes, one at a time, against a server I'd never tested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/admin/accounts&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/admin/accounts&lt;/code&gt; (the fallback path) — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/instance&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/admin/accounts/{id}/reject&lt;/code&gt; — 200&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the question that actually determined whether the product is useful on GoToSocial at all: &lt;strong&gt;does the admin API expose the signup IP?&lt;/strong&gt; Mastodon does. If GoToSocial didn't, then on GTS instances Sentinel would be running on username and application text alone — still something, but a fraction of the defense, and I'd have to say so out loud in the documentation.&lt;/p&gt;

&lt;p&gt;It does. The &lt;code&gt;ip&lt;/code&gt; field came back populated. Velocity, subnet, and reputation all function. The compatibility claim survives without a caveat, and — this is the part that matters — it survives because I ran it, not because I assumed it.&lt;/p&gt;

&lt;p&gt;I also found two differences worth documenting. GoToSocial places a signup into the pending queue immediately, without waiting for email confirmation, which Mastodon doesn't. And its admin CLI has no &lt;code&gt;delete&lt;/code&gt; for accounts at all — only &lt;code&gt;disable&lt;/code&gt;, plus rejection through the API. Neither breaks anything. Both would have been surprises in front of a customer.&lt;/p&gt;

&lt;p&gt;The first real signup&lt;/p&gt;

&lt;p&gt;Then I registered a throwaway account on my own instance, in an incognito window, like a stranger would, and wrote a genuine sentence in the reason field.&lt;/p&gt;

&lt;p&gt;Thirty seconds later the sidecar picked it out of the queue, sent it to the live screening service, and logged:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WOULD APPROVE: @[redacted] verdict=pass confidence=1 — Clean IP history and natural application language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dry run, so it did nothing, which is exactly what dry run is for. But the whole chain had run for the first time in its life: pending signup → admin API → screening engine → verdict → action decision. Every piece I'd built separately, working together, on a server type I'd never touched.&lt;/p&gt;

&lt;p&gt;Rejecting the test account afterward returned 200 and cleared the queue to zero, which incidentally verified the last endpoint.&lt;/p&gt;

&lt;p&gt;I left it in dry run, on purpose&lt;/p&gt;

&lt;p&gt;The obvious next move is flipping it live so my own instance is auto-screened. I didn't.&lt;/p&gt;

&lt;p&gt;My instance isn't a community — it's one account and a testbed. Turning on auto-approval means strangers get accounts, which means I'm hosting federated content, which means I've added a moderation surface to a machine already running five production services. That's a real decision with real obligations, not a config flag, and I'm not making it as a side effect of testing something.&lt;/p&gt;

&lt;p&gt;So the sidecar runs continuously in dry run: every future signup gets screened and logged, giving me the observations, while approval stays a human decision. The testbed value is fully there. The obligation isn't taken on accidentally. I can flip it whenever I mean to.&lt;/p&gt;

&lt;p&gt;Honest limitations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One instance, one signup, my own IP.&lt;/strong&gt; This proves the plumbing connects. It is not a load test, not an adversarial test, and not evidence about accuracy. Nobody has attacked this instance, because nobody knows it exists yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One version.&lt;/strong&gt; GoToSocial 0.22.1. API coverage moves between releases; a claim verified today is a claim about today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Fediverse-compatible" is still an overclaim, and I'm retiring it.&lt;/strong&gt; What I can now say is: Mastodon and GoToSocial, verified. Akkoma, Pleroma, Misskey, Sharkey, Iceshrimp — untested. If you run one of those and want to find out with me, I'll do the work with you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;nowasm&lt;/code&gt; build is officially unsupported&lt;/strong&gt; for media processing. Fine for a testbed. Worth knowing before you put it under a real community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I use AI tooling heavily&lt;/strong&gt; — Claude, mostly — to build and to write, including this post. The screening engine itself is deliberately heuristic and rule-based with no model in the loop, because it has to run at effectively zero marginal cost for instances that can't pay for spam protection. Where the machines helped, they helped; where the evidence comes from, it's my own server's logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point of being customer zero&lt;/p&gt;

&lt;p&gt;"Built for ourselves, available to you" has been the pitch since the safety modules were extracted from my own platform. This week it got more literal: my platform's registration is screened by this product, and now a second server type is too, on infrastructure I run.&lt;/p&gt;

&lt;p&gt;The reason that matters isn't marketing. It's that every gap I just described — the CPU, the blinded IPs, the queue behavior, the missing CLI verb — would otherwise have been found by an admin under attack, at the worst possible moment, in a system they'd trusted on my word. Finding them cost me an evening and zero dollars. Finding them the other way costs someone their instance.&lt;/p&gt;

&lt;p&gt;If you run a Fediverse instance of any flavor and your signup queue is full of garbage, Sentinel Signup is free during beta: signup.candortheopenfeednetwork.com. If you run something I haven't verified, tell me and let's test it properly — &lt;a href="mailto:tips@candortheopenfeednetwork.com"&gt;tips@candortheopenfeednetwork.com&lt;/a&gt;. I answer everything myself.&lt;br&gt;
(&lt;a href="https://candortheopenfeednetwork.com/sentinel-signup" rel="noopener noreferrer"&gt;https://candortheopenfeednetwork.com/sentinel-signup&lt;/a&gt;)&lt;br&gt;
(&lt;a href="https://app.candortheopenfeednetwork.com" rel="noopener noreferrer"&gt;https://app.candortheopenfeednetwork.com&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>architecture</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>El Formulario de Registro Que No Tiene Nada Que Filtrar</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:32:36 +0000</pubDate>
      <link>https://dev.to/candornetwork/el-formulario-de-registro-que-no-tiene-nada-que-filtrar-1i1b</link>
      <guid>https://dev.to/candornetwork/el-formulario-de-registro-que-no-tiene-nada-que-filtrar-1i1b</guid>
      <description>&lt;p&gt;Por Ronny Cruz, fundador de Open Feed Network (Candor Network). Fundador solo, una plataforma en producción, y un flujo de registro que deliberadamente no sabe casi nada de ti.&lt;/p&gt;

&lt;p&gt;El problema que me creé yo mismo&lt;/p&gt;

&lt;p&gt;El registro de Candor no recoge correo electrónico. Ni teléfono. Ni cuenta de CAPTCHA, ni identidad de OAuth, nada que verificar porque no hay nada contra qué verificar. Instalas la app, eliges un nombre de usuario, pones un PIN, y el navegador genera un par de llaves ECDSA P-256 en tu dispositivo. El servidor recibe exactamente dos cosas: el nombre de usuario y la llave pública. Las doce palabras semilla y el archivo de recuperación se quedan contigo. Ese es el registro completo.&lt;/p&gt;

&lt;p&gt;Lo construí así a propósito. Registro sin datos personales significa que no hay base de datos de correos que filtrar, ni lista de teléfonos que citar judicialmente, ni grafo de identidad que vender. Para una plataforma cuya tesis completa es que tu voz no debería depender de confiar en mí, es el diseño correcto.&lt;/p&gt;

&lt;p&gt;También es, desde la perspectiva de la defensa contra spam, una pesadilla que me entregué a mí mismo. Cada señal en la que la industria se apoya para filtrar registros —dominios de correo desechable, reputación de correo, verificación de ida y vuelta— asume que existe un correo. El mío no. Y el flujo es rápido por diseño, lo cual significa que un script también puede serlo. El viernes pasado, una revisión de código con IA lo dijo sin rodeos: nada impedía que un bot hiciera un bucle sobre el endpoint de registro y creara cincuenta mil cuentas, inundando el feed con identidades que no cuestan nada crear.&lt;/p&gt;

&lt;p&gt;La revisión tenía razón. Nada lo impedía.&lt;/p&gt;

&lt;p&gt;La ironía era que yo vendo la solución&lt;/p&gt;

&lt;p&gt;La semana anterior había lanzado Sentinel Signup — un servicio de filtrado de registros para instancias del Fediverso golpeadas por exactamente ese tipo de ola. Seis capas: reputación de IP y de dominio, velocidad por subred, patrones de tiempo, análisis de contenido, todo.&lt;/p&gt;

&lt;p&gt;Mi propia plataforma no lo estaba usando. Los hijos del zapatero, descalzos como siempre.&lt;/p&gt;

&lt;p&gt;Peor: no podía usarlo. La API de filtrado exigía un correo en cada petición, porque todos los clientes que había imaginado tenían uno. Mi propio flujo de registro —el más respetuoso con la privacidad que conozco— estaba estructuralmente excluido de mi propio producto de seguridad. Y el motor iba más lejos que solo exigirlo: un correo ausente sumaba una penalización de +30 por "correo inválido". Pasa a mis propios usuarios honestos por mi propio filtro, y todos empiezan pareciendo sospechosos. Una función de privacidad leída como señal de fraude.&lt;/p&gt;

&lt;p&gt;Vale la pena detenerse ahí, porque no es una peculiaridad de mi código. Es lo que pasa en toda esta industria cuando los sistemas anti-abuso asumen señales de identidad que las plataformas respetuosas con la privacidad deliberadamente no recogen. La postura por defecto de la defensa contra spam es que saber menos de un usuario lo hace más sospechoso. Si queremos que existan plataformas que preserven la privacidad, las herramientas de seguridad tienen que dejar de castigarlas por ello.&lt;/p&gt;

&lt;p&gt;La solución: ausencia no es malformación&lt;/p&gt;

&lt;p&gt;El cambio fue pequeño y filosóficamente importante. El motor ahora distingue dos cosas que antes confundía:&lt;/p&gt;

&lt;p&gt;Correo presente pero mal formado — se sigue penalizando. Alguien mandó basura en un campo de correo; eso es una señal real.&lt;br&gt;
Correo no proporcionado en absoluto — se registra como un hecho neutral, peso cero. Una plataforma sin datos personales no tiene nada que enviar, y ser honesta sobre eso no debería costarle nada a sus usuarios.&lt;br&gt;
Cada verificación que se activa cuando el correo sí está presente —listas de dominios desechables, patrones de correos temporales— se activa exactamente igual que antes. La batería de pruebas comprueba ambas direcciones: ninguna penalización por ausencia, ninguna suavización ante la basura.&lt;/p&gt;

&lt;p&gt;Con eso, mi registro por fin pudo ser filtrado por mi propio producto, usando solo lo que realmente existe: nombre de usuario, IP real del cliente (desde la cabecera reenviada por el CDN — el servicio nunca ve conexiones directas), agente de usuario y tiempo. Sin correo, sin marcador de posición sintético contaminando el análisis. Lo que es real se analiza; lo que no existe no se inventa.&lt;/p&gt;

&lt;p&gt;La pregunta de postura que nadie hace hasta que hay una caída&lt;/p&gt;

&lt;p&gt;Conectar el filtro al manejador de registro obligó a una decisión que la mayoría de las integraciones se saltan: ¿qué pasa cuando el filtrado mismo falla?&lt;/p&gt;

&lt;p&gt;Para mis clientes administradores de Mastodon, la respuesta es falla-a-cola — un error de filtrado envía al solicitante a la cola de aprobación humana, donde decide un moderador. Nunca admitido en silencio, nunca rechazado en silencio.&lt;/p&gt;

&lt;p&gt;Mi flujo de registro no tiene cola. No hay moderador entre una persona y su cuenta, por diseño. Así que falla-a-cola no significa nada aquí, y las dos opciones de manual están mal: fallar-cerrado significa que un tropiezo de dos segundos deja fuera a personas reales; fallar-abierto en silencio significa que los bots pasan y nadie se entera.&lt;/p&gt;

&lt;p&gt;La postura que desplegué: bloquear solo ante un veredicto duro de bloqueo; ante una marca o cualquier error, permitir — pero en voz alta. Cada registro marcado y cada fallo de filtrado queda en los logs con su razón. Una ráfaga que se cuele durante una caída es visible y atribuible en minutos. Una persona real bloqueada por un fallo de dependencia sería invisible para siempre — simplemente se iría. Entre esos dos costos, la decisión no está reñida.&lt;/p&gt;

&lt;p&gt;Qué le hizo la barrera al escenario del bot&lt;/p&gt;

&lt;p&gt;Después de desplegar, corrí el ataque contra mi propio servidor de producción. Primero, un registro legítimo — pasó limpio, token emitido, indistinguible de antes. Luego la versión en miniatura del escenario de las cincuenta mil cuentas: registros a toda velocidad desde una sola IP, par de llaves nuevo cada vez.&lt;/p&gt;

&lt;p&gt;La escalera de escalado, directo de los logs:&lt;/p&gt;

&lt;p&gt;Registros 1–2: pasaron.&lt;br&gt;
Registro 3: marcado — múltiples cuentas desde una IP, alta velocidad de subred — permitido, registrado.&lt;br&gt;
Registro 4: bloqueado — umbral de velocidad superado.&lt;br&gt;
Registros 5 al 15: bloqueados al instante — "IP previamente bloqueada". La barrera recordó. Cada intento posterior murió sin siquiera ser evaluado de nuevo.&lt;br&gt;
Un bot que quería cincuenta mil cuentas consiguió cuatro. Y la persona que se registró primero nunca notó que pasara nada.&lt;/p&gt;

&lt;p&gt;Limitaciones honestas&lt;/p&gt;

&lt;p&gt;Mi prueba fue desde una sola IP. Un adversario real rota por proxies residenciales, y la velocidad desde la misma IP se debilita frente a eso. La velocidad por subred atrapa la versión perezosa; una botnet bien distribuida es un problema más difícil, y no pretendo haberlo cerrado.&lt;br&gt;
Las señales que quedan son delgadas por diseño. Nombre de usuario, IP, agente de usuario, tiempo — ese es todo el conjunto cuando te niegas a recoger identidad. Considero que el intercambio vale la pena, pero es un intercambio, y quien te diga que puede reemplazar por completo la reputación de correo con cuatro campos te está vendiendo algo.&lt;br&gt;
Los umbrales se quedan privados. Ya escribí antes sobre por qué los mensajes de rechazo nunca deben enseñarle al atacante qué lo delató; la misma regla aplica aquí. Un registro bloqueado ve "temporalmente no disponible", no qué capa se activó ni con qué conteo.&lt;br&gt;
Esto es una plataforma, con días en producción. La evidencia son mis propios logs, fechados, de mi propio servidor. No es un estudio a escala de flota.&lt;br&gt;
Por qué el punto era comernos nuestra propia comida&lt;/p&gt;

&lt;p&gt;La propuesta de Sentinel Signup a los administradores de Mastodon siempre ha sido "construido para nosotros, disponible para ti" — los módulos se extrajeron de las defensas de la propia plataforma. Desde esta semana eso es cierto en el sentido más pleno: el mismo motor de filtrado, la misma API, el mismo sistema de inquilinos que recibe cualquier cliente es lo que está entre mi propio endpoint de registro y la próxima ola de creación masiva. El cliente cero soy yo. Cuando se rompa, se me rompe a mí primero — que es exactamente el arreglo que mantiene honesto a un producto de seguridad.&lt;/p&gt;

&lt;p&gt;Si administras una plataforma con un flujo de registro —especialmente una que recoge menos de lo que la industria asume que deberías— Sentinel Signup es gratis durante la beta en signup.candortheopenfeednetwork.com, y el soporte sin datos personales descrito aquí está activo para todos los inquilinos. Si crees que mi postura ante fallos está equivocada, quiero escucharlo especialmente: &lt;a href="mailto:tips@candortheopenfeednetwork.com"&gt;tips@candortheopenfeednetwork.com&lt;/a&gt;. Respondo todo yo mismo.&lt;/p&gt;

</description>
      <category>security</category>
      <category>discuss</category>
      <category>claude</category>
      <category>architecture</category>
    </item>
    <item>
      <title>La Compatibilidad Que No Me Había Ganado</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:28:34 +0000</pubDate>
      <link>https://dev.to/candornetwork/la-compatibilidad-que-no-me-habia-ganado-gan</link>
      <guid>https://dev.to/candornetwork/la-compatibilidad-que-no-me-habia-ganado-gan</guid>
      <description>&lt;p&gt;Por Ronny Cruz, fundador de Open Feed Network (Candor Network). Fundador solo, una plataforma en producción y —desde esta semana— una instancia muy pequeña del Fediverso.&lt;/p&gt;

&lt;p&gt;La línea en mi propio README&lt;/p&gt;

&lt;p&gt;Sentinel Signup es un servicio de filtrado de registros para instancias del Fediverso. Lee tu cola de solicitudes pendientes, evalúa a cada solicitante, y aprueba o rechaza a través de la API de administración. Lo lancé durante la ola de spam de julio, cuando los administradores de instancias se estaban ahogando en solicitudes falsas.&lt;/p&gt;

&lt;p&gt;Esta era la instrucción de configuración que envié con él:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mastodon: Preferencias → Desarrollo → Nueva aplicación, permisos &lt;code&gt;admin:read admin:write&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Léelo otra vez. El producto dice &lt;em&gt;Fediverso&lt;/em&gt;. La documentación dice &lt;em&gt;Mastodon&lt;/em&gt;. No son la misma palabra, y la distancia entre ambas era una afirmación para la que no tenía evidencia.&lt;/p&gt;

&lt;p&gt;El Fediverso no es un solo programa. Es Mastodon y GoToSocial y Akkoma y Pleroma y Misskey y Sharkey e Iceshrimp y una docena más, la mayoría de los cuales implementan "la API cliente de Mastodon" en grados variables y no documentados. Mi sidecar hablaba cuatro endpoints de esa API. Los había verificado contra exactamente cero servidores, porque nunca en mi vida había administrado una instancia.&lt;/p&gt;

&lt;p&gt;Estaba vendiendo herramientas anti-abuso a una comunidad de la que no era miembro. No es un fallo moral, pero sí es uno epistémico: todo lo que sabía sobre cómo se comportan esos servidores lo sabía por leer especificaciones. Las especificaciones describen intenciones. Los despliegues tienen opiniones.&lt;/p&gt;

&lt;p&gt;Así que construí el banco de pruebas que debí haber tenido primero&lt;/p&gt;

&lt;p&gt;La restricción era que no podía costar nada. Mastodon es un inquilino pesado —Ruby, Sidekiq, Redis, su propio Postgres— y la máquina donde lo pondría ya corre cinco servicios en producción. GoToSocial es un solo binario en Go con SQLite que reposa en un par de cientos de megabytes, e implementa la API cliente de Mastodon. Esa es justo la razón por la que era la elección correcta: &lt;em&gt;no&lt;/em&gt; es Mastodon, lo que lo convierte en una prueba real y no en un ensayo.&lt;/p&gt;

&lt;p&gt;Dos cosas salieron mal antes de que llegara a arrancar, y ambas vale la pena anotarlas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La CPU se negó.&lt;/strong&gt; La compilación estándar de GoToSocial ejecuta su ffmpeg y su SQLite integrados a través de wazero, un runtime de WebAssembly, y el compilador de wazero requiere instrucciones x86-64-v2. Mi VPS se reporta como una CPU virtual genérica de QEMU —sin SSE4.2, sin SSSE3, sin POPCNT— porque eso es lo que anuncian muchos hosts virtualizados económicos o antiguos. GoToSocial no degrada a modo intérprete; lanza un pánico a propósito, cosa que respeto. La solución es una compilación oficial &lt;code&gt;nowasm&lt;/code&gt; que usa el ffmpeg nativo del sistema. Si te autoalojas en virtualización barata, revisa &lt;code&gt;/proc/cpuinfo&lt;/code&gt; antes de asumir instrucciones modernas. La mía llevaba meses siendo silenciosamente antigua.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El proxy mintió sobre quién tocaba la puerta.&lt;/strong&gt; La instancia está detrás de Cloudflare, y mi configuración de nginx reenviaba la dirección del cliente de la forma convencional. Detrás de un CDN, esa dirección es la del CDN — cada registro del planeta habría llegado desde el mismo puñado de IPs de borde.&lt;/p&gt;

&lt;p&gt;Ese merece detenerse, por &lt;em&gt;cómo&lt;/em&gt; falla. Nada da error. El servicio de filtrado sigue devolviendo veredictos. Los logs siguen viéndose sanos. Simplemente se vuelve ciego en exactamente la dimensión donde más vale: reputación de IP, velocidad por subred, detección de ráfagas — todas las señales que atrapan una granja de bots colapsan en "una dirección, siempre". Un sistema de seguridad que reporta éxito mientras no mide nada es peor que uno que está obviamente roto, lección que sigo reaprendiendo con disfraces nuevos. La solución fue una directiva de nginx leyendo la cabecera de cliente real del CDN. El hábito es: después de cualquier cambio de infraestructura, verifica que tus señales sigan llevando información, no solo que tus peticiones sigan devolviendo 200.&lt;/p&gt;

&lt;p&gt;Cómo se vio la verificación de verdad&lt;/p&gt;

&lt;p&gt;Con el nodo corriendo y accesible, revisé las cuatro llamadas que hace mi sidecar, una por una, contra un servidor que nunca había probado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/admin/accounts&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/admin/accounts&lt;/code&gt; (la ruta de respaldo) — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/instance&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/admin/accounts/{id}/reject&lt;/code&gt; — 200&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luego la pregunta que realmente determinaba si el producto sirve de algo en GoToSocial: &lt;strong&gt;¿expone la API de administración la IP del registro?&lt;/strong&gt; Mastodon sí. Si GoToSocial no lo hiciera, entonces en instancias GTS Sentinel estaría funcionando solo con el nombre de usuario y el texto de la solicitud — algo, pero una fracción de la defensa, y tendría que decirlo en voz alta en la documentación.&lt;/p&gt;

&lt;p&gt;Sí la expone. El campo &lt;code&gt;ip&lt;/code&gt; volvió con valor. Velocidad, subred y reputación funcionan. La afirmación de compatibilidad sobrevive sin matices y —esto es lo que importa— sobrevive porque la ejecuté, no porque la asumí.&lt;/p&gt;

&lt;p&gt;También encontré dos diferencias que vale documentar. GoToSocial coloca una solicitud en la cola pendiente de inmediato, sin esperar confirmación de correo, cosa que Mastodon no hace. Y su CLI de administración no tiene &lt;code&gt;delete&lt;/code&gt; para cuentas en absoluto — solo &lt;code&gt;disable&lt;/code&gt;, más el rechazo vía API. Ninguna de las dos rompe nada. Ambas habrían sido sorpresas frente a un cliente.&lt;/p&gt;

&lt;p&gt;El primer registro real&lt;/p&gt;

&lt;p&gt;Después registré una cuenta desechable en mi propia instancia, en una ventana de incógnito, como lo haría un desconocido, y escribí una frase genuina en el campo de motivo.&lt;/p&gt;

&lt;p&gt;Treinta segundos más tarde, el sidecar la sacó de la cola, la envió al servicio de filtrado en vivo y registró:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;APROBARÍA: @[redactado] veredicto=pass confianza=1 — Historial de IP limpio y lenguaje de solicitud natural.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modo de prueba, así que no hizo nada, que es exactamente para lo que sirve el modo de prueba. Pero la cadena completa había corrido por primera vez en su vida: solicitud pendiente → API de administración → motor de filtrado → veredicto → decisión de acción. Cada pieza que había construido por separado, funcionando junta, en un tipo de servidor que nunca había tocado.&lt;/p&gt;

&lt;p&gt;Rechazar la cuenta de prueba después devolvió 200 y dejó la cola en cero, lo que de paso verificó el último endpoint.&lt;/p&gt;

&lt;p&gt;Lo dejé en modo de prueba, a propósito&lt;/p&gt;

&lt;p&gt;El siguiente movimiento obvio es activarlo para que mi propia instancia se filtre automáticamente. No lo hice.&lt;/p&gt;

&lt;p&gt;Mi instancia no es una comunidad — es una cuenta y un banco de pruebas. Encender la aprobación automática significa que desconocidos consiguen cuentas, lo que significa que estoy alojando contenido federado, lo que significa que le he añadido una superficie de moderación a una máquina que ya corre cinco servicios en producción. Esa es una decisión real con obligaciones reales, no una bandera de configuración, y no la voy a tomar como efecto secundario de probar algo.&lt;/p&gt;

&lt;p&gt;Así que el sidecar corre continuamente en modo de prueba: cada registro futuro se filtra y se registra, dándome las observaciones, mientras la aprobación sigue siendo una decisión humana. El valor del banco de pruebas está completo. La obligación no se asume por accidente. Puedo activarlo cuando lo decida de verdad.&lt;/p&gt;

&lt;p&gt;Limitaciones honestas&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Una instancia, un registro, mi propia IP.&lt;/strong&gt; Esto prueba que la tubería conecta. No es una prueba de carga, ni una prueba adversaria, ni evidencia sobre precisión. Nadie ha atacado esta instancia, porque nadie sabe todavía que existe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Una versión.&lt;/strong&gt; GoToSocial 0.22.1. La cobertura de la API se mueve entre versiones; una afirmación verificada hoy es una afirmación sobre hoy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Compatible con el Fediverso" sigue siendo excesivo, y lo retiro.&lt;/strong&gt; Lo que ahora puedo decir es: Mastodon y GoToSocial, verificados. Akkoma, Pleroma, Misskey, Sharkey, Iceshrimp — sin probar. Si administras alguno de esos y quieres averiguarlo conmigo, hago el trabajo contigo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La compilación &lt;code&gt;nowasm&lt;/code&gt; no tiene soporte oficial&lt;/strong&gt; para procesamiento de medios. Bien para un banco de pruebas. Vale saberlo antes de ponerla bajo una comunidad real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uso herramientas de IA intensivamente&lt;/strong&gt; —Claude, sobre todo— para construir y para escribir, incluido este texto. El motor de filtrado en sí es deliberadamente heurístico y basado en reglas, sin modelo en el ciclo, porque tiene que funcionar a un costo marginal efectivamente cero para instancias que no pueden pagar por protección contra spam. Donde las máquinas ayudaron, ayudaron; de donde viene la evidencia, son los logs de mi propio servidor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El punto de ser el cliente cero&lt;/p&gt;

&lt;p&gt;"Construido para nosotros, disponible para ti" ha sido la propuesta desde que los módulos de seguridad se extrajeron de mi propia plataforma. Esta semana se volvió más literal: el registro de mi plataforma está filtrado por este producto, y ahora un segundo tipo de servidor también, sobre infraestructura que yo administro.&lt;/p&gt;

&lt;p&gt;La razón por la que eso importa no es de mercadeo. Es que cada hueco que acabo de describir —la CPU, las IPs cegadas, el comportamiento de la cola, el verbo faltante en la CLI— lo habría encontrado, de otro modo, un administrador bajo ataque, en el peor momento posible, en un sistema que había confiado por mi palabra. Encontrarlos me costó una tarde y cero dólares. Encontrarlos del otro modo le cuesta a alguien su instancia.&lt;/p&gt;

&lt;p&gt;Si administras una instancia del Fediverso de cualquier sabor y tu cola de registros está llena de basura, Sentinel Signup es gratis durante la beta: signup.candortheopenfeednetwork.com. Si corres algo que no he verificado, dímelo y lo probamos como se debe — &lt;a href="mailto:tips@candortheopenfeednetwork.com"&gt;tips@candortheopenfeednetwork.com&lt;/a&gt;. Respondo todo yo mismo.&lt;/p&gt;

</description>
      <category>espanol</category>
      <category>ai</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Compatibility Claim I Hadn't Earned</title>
      <dc:creator>Ronny Cruz</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:20:05 +0000</pubDate>
      <link>https://dev.to/candornetwork/the-compatibility-claim-i-hadnt-earned-32on</link>
      <guid>https://dev.to/candornetwork/the-compatibility-claim-i-hadnt-earned-32on</guid>
      <description>&lt;p&gt;The Compatibility Claim I Hadn't Earned&lt;/p&gt;

&lt;p&gt;By Ronny Cruz Alvarez, founder of Open Feed Network (Candor Network). Solo founder, one production platform, and — as of this week — one very small Fediverse instance.&lt;/p&gt;

&lt;p&gt;The line in my own README&lt;/p&gt;

&lt;p&gt;Sentinel Signup is a registration-screening service for Fediverse instances. It reads your pending signup queue, scores each applicant, and approves or rejects through the admin API. I launched it during the July spam wave, when instance admins were drowning in fake approval requests.&lt;/p&gt;

&lt;p&gt;Here's the setup instruction I shipped with it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mastodon: Preferences → Development → New application, scopes &lt;code&gt;admin:read admin:write&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that again. The product says &lt;em&gt;Fediverse&lt;/em&gt;. The documentation says &lt;em&gt;Mastodon&lt;/em&gt;. Those are not the same word, and the gap between them was a claim I had no evidence for.&lt;/p&gt;

&lt;p&gt;The Fediverse is not one piece of software. It's Mastodon and GoToSocial and Akkoma and Pleroma and Misskey and Sharkey and Iceshrimp and a dozen others, most of which implement "the Mastodon client API" to varying and undocumented degrees. My sidecar spoke four endpoints of that API. I had verified them against exactly zero servers, because I had never run an instance in my life.&lt;/p&gt;

&lt;p&gt;I was selling anti-abuse tooling to a community I wasn't a member of. That's not a moral failing, but it is an epistemic one: everything I knew about how those servers behave, I knew from reading specifications. Specifications describe intent. Deployments have opinions.&lt;/p&gt;

&lt;p&gt;So I built the testbed I should have had first&lt;/p&gt;

&lt;p&gt;The constraint was that it had to cost nothing. Mastodon itself is a heavy tenant — Ruby, Sidekiq, Redis, its own Postgres — and the box I'd be putting it on already runs five production services. GoToSocial is a single Go binary with SQLite that idles in a couple hundred megabytes, and it implements the Mastodon client API. That's the whole reason it was the right choice: it's &lt;em&gt;not&lt;/em&gt; Mastodon, which makes it a real test rather than a rehearsal.&lt;/p&gt;

&lt;p&gt;Two things went wrong before it ever started, and both are worth writing down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CPU refused.&lt;/strong&gt; GoToSocial's standard build runs its bundled ffmpeg and SQLite through wazero, a WebAssembly runtime, and wazero's compiler requires x86-64-v2 instructions. My VPS reports itself as a generic QEMU virtual CPU — no SSE4.2, no SSSE3, no POPCNT — because that's what many budget and older virtualized hosts advertise. GoToSocial doesn't degrade to interpreter mode; it panics on purpose, which I respect. The fix is an official &lt;code&gt;nowasm&lt;/code&gt; build that uses the system's native ffmpeg instead. If you're self-hosting anything on inexpensive virtualization, check &lt;code&gt;/proc/cpuinfo&lt;/code&gt; before you assume modern instruction sets. Mine had been quietly ancient for months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The proxy lied about who was knocking.&lt;/strong&gt; The instance sits behind Cloudflare, and my nginx config forwarded the client address the conventional way. Behind a CDN, that address is the CDN's — every signup on Earth would have arrived from the same handful of edge IPs.&lt;/p&gt;

&lt;p&gt;That one is worth dwelling on, because of how it fails. Nothing errors. The screening service still returns verdicts. The logs still look healthy. It just silently becomes blind in the exact dimension it's most valuable in: IP reputation, subnet velocity, burst detection — all the signals that catch a bot farm — collapse into "one address, always." A safety system that reports success while measuring nothing is worse than one that's obviously broken, which is a lesson I keep relearning in new costumes. The fix was one nginx directive reading the CDN's real-client header. The habit is: after any infrastructure change, verify that your signals still carry information, not just that your requests still return 200.&lt;/p&gt;

&lt;p&gt;What verification actually looked like&lt;/p&gt;

&lt;p&gt;With the node running and reachable, I checked the four calls my sidecar makes, one at a time, against a server I'd never tested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/admin/accounts&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/admin/accounts&lt;/code&gt; (the fallback path) — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v2/instance&lt;/code&gt; — 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/admin/accounts/{id}/reject&lt;/code&gt; — 200&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the question that actually determined whether the product is useful on GoToSocial at all: &lt;strong&gt;does the admin API expose the signup IP?&lt;/strong&gt; Mastodon does. If GoToSocial didn't, then on GTS instances Sentinel would be running on username and application text alone — still something, but a fraction of the defense, and I'd have to say so out loud in the documentation.&lt;/p&gt;

&lt;p&gt;It does. The &lt;code&gt;ip&lt;/code&gt; field came back populated. Velocity, subnet, and reputation all function. The compatibility claim survives without a caveat, and — this is the part that matters — it survives because I ran it, not because I assumed it.&lt;/p&gt;

&lt;p&gt;I also found two differences worth documenting. GoToSocial places a signup into the pending queue immediately, without waiting for email confirmation, which Mastodon doesn't. And its admin CLI has no &lt;code&gt;delete&lt;/code&gt; for accounts at all — only &lt;code&gt;disable&lt;/code&gt;, plus rejection through the API. Neither breaks anything. Both would have been surprises in front of a customer.&lt;/p&gt;

&lt;p&gt;The first real signup&lt;/p&gt;

&lt;p&gt;Then I registered a throwaway account on my own instance, in an incognito window, like a stranger would, and wrote a genuine sentence in the reason field.&lt;/p&gt;

&lt;p&gt;Thirty seconds later the sidecar picked it out of the queue, sent it to the live screening service, and logged:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WOULD APPROVE: @[redacted] verdict=pass confidence=1 — Clean IP history and natural application language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dry run, so it did nothing, which is exactly what dry run is for. But the whole chain had run for the first time in its life: pending signup → admin API → screening engine → verdict → action decision. Every piece I'd built separately, working together, on a server type I'd never touched.&lt;/p&gt;

&lt;p&gt;Rejecting the test account afterward returned 200 and cleared the queue to zero, which incidentally verified the last endpoint.&lt;/p&gt;

&lt;p&gt;I left it in dry run, on purpose&lt;/p&gt;

&lt;p&gt;The obvious next move is flipping it live so my own instance is auto-screened. I didn't.&lt;/p&gt;

&lt;p&gt;My instance isn't a community — it's one account and a testbed. Turning on auto-approval means strangers get accounts, which means I'm hosting federated content, which means I've added a moderation surface to a machine already running five production services. That's a real decision with real obligations, not a config flag, and I'm not making it as a side effect of testing something.&lt;/p&gt;

&lt;p&gt;So the sidecar runs continuously in dry run: every future signup gets screened and logged, giving me the observations, while approval stays a human decision. The testbed value is fully there. The obligation isn't taken on accidentally. I can flip it whenever I mean to.&lt;/p&gt;

&lt;p&gt;Honest limitations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One instance, one signup, my own IP.&lt;/strong&gt; This proves the plumbing connects. It is not a load test, not an adversarial test, and not evidence about accuracy. Nobody has attacked this instance, because nobody knows it exists yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One version.&lt;/strong&gt; GoToSocial 0.22.1. API coverage moves between releases; a claim verified today is a claim about today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Fediverse-compatible" is still an overclaim, and I'm retiring it.&lt;/strong&gt; What I can now say is: Mastodon and GoToSocial, verified. Akkoma, Pleroma, Misskey, Sharkey, Iceshrimp — untested. If you run one of those and want to find out with me, I'll do the work with you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;nowasm&lt;/code&gt; build is officially unsupported&lt;/strong&gt; for media processing. Fine for a testbed. Worth knowing before you put it under a real community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I use AI tooling heavily&lt;/strong&gt; — Claude, mostly — to build and to write, including this post. The screening engine itself is deliberately heuristic and rule-based with no model in the loop, because it has to run at effectively zero marginal cost for instances that can't pay for spam protection. Where the machines helped, they helped; where the evidence comes from, it's my own server's logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point of being customer zero&lt;/p&gt;

&lt;p&gt;"Built for ourselves, available to you" has been the pitch since the safety modules were extracted from my own platform. This week it got more literal: my platform's registration is screened by this product, and now a second server type is too, on infrastructure I run.&lt;/p&gt;

&lt;p&gt;The reason that matters isn't marketing. It's that every gap I just described — the CPU, the blinded IPs, the queue behavior, the missing CLI verb — would otherwise have been found by an admin under attack, at the worst possible moment, in a system they'd trusted on my word. Finding them cost me an evening and zero dollars. Finding them the other way costs someone their instance.&lt;/p&gt;

&lt;p&gt;If you run a Fediverse instance of any flavor and your signup queue is full of garbage, Sentinel Signup is free during beta: signup.candortheopenfeednetwork.com. If you run something I haven't verified, tell me and let's test it properly — &lt;a href="mailto:tips@candortheopenfeednetwork.com"&gt;tips@candortheopenfeednetwork.com&lt;/a&gt;. I answer everything myself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>security</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
