<?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: Aryan</title>
    <description>The latest articles on DEV Community by Aryan (@aryanxp1).</description>
    <link>https://dev.to/aryanxp1</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%2F4108436%2F273865d1-95d4-4cd1-9609-678ac1e88377.jpg</url>
      <title>DEV Community: Aryan</title>
      <link>https://dev.to/aryanxp1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aryanxp1"/>
    <language>en</language>
    <item>
      <title>I Built a Hackathon Platform with Python, SQLite, and Zero Runtime Dependencies</title>
      <dc:creator>Aryan</dc:creator>
      <pubDate>Sun, 04 Oct 2026 15:05:02 +0000</pubDate>
      <link>https://dev.to/aryanxp1/i-built-a-hackathon-platform-with-python-sqlite-and-zero-runtime-dependencies-57i5</link>
      <guid>https://dev.to/aryanxp1/i-built-a-hackathon-platform-with-python-sqlite-and-zero-runtime-dependencies-57i5</guid>
      <description>&lt;p&gt;What if, instead of building a project &lt;em&gt;for&lt;/em&gt; a hackathon, you had to build the platform that actually judges the hackathon?&lt;/p&gt;

&lt;p&gt;That was the prompt behind &lt;strong&gt;DOGFOOD 2026&lt;/strong&gt;: &lt;em&gt;"Build the platform that will judge you."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most hackathon projects are quick prototypes with forty npm packages, three mock APIs, and a prayer. For this challenge, I decided to go in the exact opposite direction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One Python process. One SQLite database. Zero runtime third-party dependencies. Zero outbound network calls.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No Flask. No FastAPI. No Django. No Redis. No PostgreSQL. No CDN.&lt;/p&gt;

&lt;p&gt;Just Python's standard library, SQLite, vanilla CSS/JS, and Docker.&lt;/p&gt;

&lt;p&gt;Here is the story of how I built &lt;strong&gt;&lt;a href="https://github.com/Aryanxp1/Lockdown" rel="noopener noreferrer"&gt;Lockdown&lt;/a&gt;&lt;/strong&gt;, the architectural hurdles I ran into, and why extreme constraints made me a significantly better engineer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://lockdown-1-3tuu.onrender.com/" rel="noopener noreferrer"&gt;lockdown-1-3tuu.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📺 &lt;strong&gt;Video Walkthrough:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=ZfXDbT199II" rel="noopener noreferrer"&gt;Watch the demo on YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎬 Lockdown in 30 Seconds
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/ssapable"&gt;@ssapable&lt;/a&gt;&lt;/strong&gt; created this short video to visually explain Lockdown and its core architecture. It was great to see the project interpreted in a completely different format, and I really appreciate the effort behind it.&lt;/p&gt;

&lt;p&gt;🎥 &lt;a href="https://youtu.be/kb6JHWUKg3M" rel="noopener noreferrer"&gt;Watch the video on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  - 💻 &lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/Aryanxp1/Lockdown" rel="noopener noreferrer"&gt;github.com/Aryanxp1/Lockdown&lt;/a&gt;
&lt;/h2&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%2F0q68nzqtgay4uj6l5uk1.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%2F0q68nzqtgay4uj6l5uk1.png" alt="Lockdown Homepage" width="800" height="380"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: The Lockdown homepage — event metrics, a six-stage pipeline rail, and recent submissions.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What is Lockdown?
&lt;/h2&gt;

&lt;p&gt;Lockdown is a self-hosted competition operating system. It handles the complete lifecycle of a hackathon:&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%2Fboc024bhl7x8z0aawyn3.jpg" 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%2Fboc024bhl7x8z0aawyn3.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple hackathons&lt;/strong&gt; in a single installation with strict data isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Participant workspaces&lt;/strong&gt; with team management and deadline-enforced submissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blind judge queues&lt;/strong&gt; with weighted rubric scoring and draft autosave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-judge statistical normalization&lt;/strong&gt; (z-scores) to balance harsh vs. lenient graders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frozen publication snapshots&lt;/strong&gt; with SHA-256 checksums and cryptographic certificates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A synchronous append-only audit trail&lt;/strong&gt; recording every authentication, refusal, score, and publication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire runtime stack fits in this diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       Browser (HTML / CSS / JS)
                                  │
                                  ▼
                    Python ThreadingHTTPServer
                                  │
          ┌───────────────────────┼───────────────────────┐
          ▼                       ▼                       ▼
    Auth &amp;amp; Sessions        State &amp;amp; Deadlines       Judging &amp;amp; Scoring
    (HMAC / scrypt)      (Window enforcement)     (Z-score / Rubrics)
          │                       │                       │
          └───────────────────────┼───────────────────────┘
                                  │
                                  ▼
                       SQLite 3 (WAL + RLock)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No external message brokers. No background worker daemons. When a request completes, its database transaction and audit log entry are already committed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Zero Dependencies?
&lt;/h2&gt;

&lt;p&gt;Using FastAPI, SQLAlchemy, and Tailwind would have made several parts faster to write. But giving up frameworks forced me to understand the fundamentals of web architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No magic routing:&lt;/strong&gt; I had to write an HTTP dispatcher on top of Python's &lt;code&gt;http.server&lt;/code&gt;, handle MIME types, enforce body upload caps (2 MiB max), parse query strings, and implement path-traversal guards from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic security:&lt;/strong&gt; Security headers (&lt;code&gt;CSP&lt;/code&gt;, &lt;code&gt;X-Frame-Options: DENY&lt;/code&gt;, &lt;code&gt;nosniff&lt;/code&gt;, &lt;code&gt;SameSite=Lax&lt;/code&gt;) aren't plugin defaults; they are explicitly applied to every outgoing response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True air-gap compliance:&lt;/strong&gt; Because &lt;code&gt;app/&lt;/code&gt; never imports &lt;code&gt;urllib.request&lt;/code&gt; or any HTTP client, the portal physically &lt;em&gt;cannot&lt;/em&gt; leak data or phone home. Running the container on an internal Docker network with no default gateway passes the entire acceptance suite without a hitch.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Architectural Challenge 1: The Multi-Hackathon Isolation Problem
&lt;/h2&gt;

&lt;p&gt;A naive hackathon portal assumes there is only ever one event. But real infrastructure needs to host multiple hackathons simultaneously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                               LOCKDOWN PLATFORM
                                       │
                    ┌──────────────────┴──────────────────┐
                    ▼                                     ▼
           Hackathon A (Active)                  Hackathon B (Embargoed)
             ├── Teams                             ├── Teams
             ├── Projects                          ├── Projects
             └── Judges                            └── Judges
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This introduced a crucial authorization question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Does an organizer have global superuser access across every competition?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided the answer had to be an uncompromising &lt;strong&gt;NO&lt;/strong&gt;. Organizer privileges are strictly scoped &lt;em&gt;per event&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;If you are an organizer for Hackathon A, navigating to Hackathon B doesn't just hide admin buttons in the template — the handler itself evaluates &lt;code&gt;events.can_manage()&lt;/code&gt; and returns a &lt;code&gt;403 Forbidden&lt;/code&gt; while synchronously appending the unauthorized attempt to the audit log.&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%2Fmxj1du92m00gjvp3oaz3.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%2Fmxj1du92m00gjvp3oaz3.png" alt="Public Project Gallery" width="800" height="451"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: The public gallery — track filter pills, instant search, and paginated project cards.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Architectural Challenge 2: Deadlines &amp;amp; Append-Only Submissions
&lt;/h2&gt;

&lt;p&gt;In many apps, submitting a project is just a database &lt;code&gt;UPDATE&lt;/code&gt;. But in competitions, submissions are legal records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens if a team submits 5 minutes before the deadline, and then edits their project 1 minute &lt;em&gt;after&lt;/em&gt; the deadline?&lt;/li&gt;
&lt;li&gt;Can a team claim their submission was altered by an organizer?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server-enforced deadlines:&lt;/strong&gt; Deadlines are evaluated inside the request handler. Disabling a submit button in JavaScript is a UX convenience, not security. If a raw &lt;code&gt;curl POST&lt;/code&gt; arrives one second past the cutoff, it is rejected with &lt;code&gt;403 submission_window_closed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned history:&lt;/strong&gt; Every project update appends a new record to &lt;code&gt;project_versions&lt;/code&gt;. Old descriptions and links are never overwritten in place.&lt;/li&gt;
&lt;/ol&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%2Ff55h6ccdir02i7r3byg5.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%2Ff55h6ccdir02i7r3byg5.png" alt="Organizer Desk" width="800" height="492"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: The Organizer Desk — stage controls, live submission metrics, and judge assignment overview.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Architectural Challenge 3: Blind Judging &amp;amp; The Calibration Math
&lt;/h2&gt;

&lt;p&gt;Judging was by far my favorite subsystem to design.&lt;/p&gt;

&lt;p&gt;In most hackathons, different judges grade on vastly different scales. One judge might give an average score of &lt;code&gt;92&lt;/code&gt; with narrow spread, while another judge gives an average of &lt;code&gt;65&lt;/code&gt; and grades ruthlessly. If you simply calculate arithmetic means, a team's final rank depends heavily on whether they drew a lenient or harsh reviewer.&lt;/p&gt;

&lt;p&gt;To fix this, Lockdown implements &lt;strong&gt;cross-judge z-score normalization&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z = (raw_score - judge_mean) / judge_stddev

normalized = clamp(75.0 + 12.0 * z, 0, 100)


This rescales all judges toward a standardized distribution with a target mean of 75.0 and standard deviation of 12.0.


And in the fallbacks bullet points right below it:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
markdown&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer than 3 completed reviews → &lt;code&gt;insufficient_sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variance / std dev &amp;lt; 2.0 → &lt;code&gt;zero_variance&lt;/code&gt;
&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%2F38x6qxm83k11cr929ohk.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%2F38x6qxm83k11cr929ohk.png" alt="Judge Calibration" width="800" height="503"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Judge calibration roster — per-judge mean, variance, and severity classification.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Explicit Fallbacks (No Fabricated Precision)
&lt;/h3&gt;

&lt;p&gt;What happens if a judge only scored two projects? Or what if a judge gave every single project an identical score of &lt;code&gt;80&lt;/code&gt; ($\sigma = 0$)?&lt;/p&gt;

&lt;p&gt;In those cases, statistical normalization breaks down. Instead of generating artificial numbers or throwing a &lt;code&gt;ZeroDivisionError&lt;/code&gt;, Lockdown defines explicit fallback flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer than 3 completed reviews $\rightarrow$ &lt;code&gt;insufficient_sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variance $\sigma &amp;lt; 2.0$ $\rightarrow$ &lt;code&gt;zero_variance&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both fall back cleanly to uncalibrated raw scores, and the project is flagged as &lt;code&gt;provisional&lt;/code&gt; on the organizer's scoreboard until more reviews arrive.&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%2F59slob6kmoi74yw3kqny.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%2F59slob6kmoi74yw3kqny.png" alt="Judge Evaluation Form" width="800" height="484"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Evaluation form — weighted criteria, draft autosave via fetch, and 1–5 keyboard shortcuts.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Architectural Challenge 4: Frozen Results vs. Live Queries
&lt;/h2&gt;

&lt;p&gt;Here is a trap many developers fall into:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Building the leaderboard as a live &lt;code&gt;SELECT ... ORDER BY score DESC&lt;/code&gt; query.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why is that a disaster? Because if an organizer edits a score or deletes a test account two weeks after the event ends, the public leaderboard silently mutates! Historical rankings get rewritten without anyone noticing.&lt;/p&gt;

&lt;p&gt;In Lockdown, &lt;strong&gt;publishing is a frozen snapshot&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internal Review Data
         │
         ▼
Calculate Final Standings (Scoring Engine)
         │
         ▼
Serialize to JSON (`rows_json`)
         │
         ▼
Compute SHA-256 Digest
         │
         ▼
Insert into `result_publications` (revision_no + 1, is_current = 1)
         │
         ▼
Issue Cryptographic Certificates &amp;amp; Commit Audit Row
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once published, the public &lt;code&gt;/results&lt;/code&gt; endpoint reads strictly from the active snapshot row. Subsequent edits do not touch historical revisions.&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%2Fsy98x2p8yckan7b2o09y.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%2Fsy98x2p8yckan7b2o09y.png" alt="Published Leaderboard" width="799" height="497"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6: Published standings leaderboard — verifiable scores, rank badges, and track awards.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Architectural Challenge 5: An Append-Only Audit Ledger
&lt;/h2&gt;

&lt;p&gt;Competition software needs to answer forensic questions: &lt;em&gt;Who changed this score? When was this review submitted? Who published the revision?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every critical mutation in Lockdown is paired with a synchronous call to &lt;code&gt;audit.record()&lt;/code&gt; inside the active SQLite transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results.publish&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actor_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;target_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;publication_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;revision_no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checksum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;checksum&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because it executes in the same transaction as the state change, an audit event cannot exist without the action, and an action cannot succeed without creating an audit entry.&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%2F58mzvxjawjgbm6rufjuy.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%2F58mzvxjawjgbm6rufjuy.png" alt="Audit Trail" width="800" height="484"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 7: The synchronous append-only audit trail.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The UI Redesign: From "1990s Paper Archive" to High-Contrast Cyber Dark
&lt;/h2&gt;

&lt;p&gt;Early in the project, the UI felt like an old editorial newspaper: cream backgrounds, serif fonts, and thin grey borders. It looked more like a law library than a modern hackathon platform.&lt;/p&gt;

&lt;p&gt;I completely overhauled the interface using &lt;strong&gt;pure Vanilla CSS&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Canvas:&lt;/strong&gt; Pitch black (&lt;code&gt;#050508&lt;/code&gt;) with layered slate cards (&lt;code&gt;#0e0e13&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure:&lt;/strong&gt; Crisp, prominent &lt;code&gt;2.5px solid #4a4e5a&lt;/code&gt; grey borders across all project cards, stat containers, and navigation bars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accents:&lt;/strong&gt; High-visibility crimson glow (&lt;code&gt;#ef4444&lt;/code&gt;) for interactive hovers and status indicators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Micro-UX:&lt;/strong&gt; 1–5 keyboard shortcuts for rapid judge scoring and a floating account switcher (&lt;code&gt;PORTAL_FAST_LOGIN=1&lt;/code&gt;) so organizers and judges can demo roles instantly without typing passwords.&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%2Fdhh2lcc656gh0ab9ozks.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%2Fdhh2lcc656gh0ab9ozks.png" alt="Judge Dashboard" width="800" height="451"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 8: Judge workspace — assignment queue, review progress bar, and evaluated submissions.&lt;/em&gt;&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%2F8vr579m5gr2m8hc9uvrq.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%2F8vr579m5gr2m8hc9uvrq.png" alt="Participant Workspace" width="799" height="475"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 9: Participant workspace — team management, deadline state, and verifiable achievement certificate.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Bug That Humbled Me: &lt;code&gt;.gitignore&lt;/code&gt; vs. Deployment
&lt;/h2&gt;

&lt;p&gt;The hardest bug wasn't the z-score math or the HTTP parser. It was a deployment facepalm.&lt;/p&gt;

&lt;p&gt;Everything worked smoothly on my laptop. Then, I tested a clean deployment inside Docker from a fresh repository checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container immediately crashed on boot.&lt;/p&gt;

&lt;p&gt;Why? Because &lt;code&gt;.dogfood.toml&lt;/code&gt; (which configures the acceptance checker and port mappings) had been matched by an overly broad &lt;code&gt;.gitignore&lt;/code&gt; pattern. It existed on my local machine, but git had never tracked it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local machine:       Everything works ✅
Fresh Git clone:     Missing configuration file ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix took thirty seconds, but the takeaway stuck with me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your laptop is never the deployment artifact. Your git repository is.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Acceptance Testing: Verifying the System
&lt;/h2&gt;

&lt;p&gt;To prove that the platform meets the DOGFOOD 2026 specification, the codebase includes an HTTP-based automated test runner (&lt;code&gt;run.py&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;portal python3 run.py .dogfood.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOGFOOD 2026 acceptance report
portal: http://localhost:8081
claimed: T1 T2
fixtures: fixtures.json

T1  gallery is public ................. PASS
T1  project from fixtures shown ....... PASS
T1  closed event refuses submissions .. PASS
T2  judge sees own scores ............. PASS
T2  judge cannot see peer scores ...... PASS
T2  participant blocked ............... PASS
T2  csv export works .................. PASS

claimed T1 T2, verified T1 T2 (7/7 probes passed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, standard &lt;code&gt;unittest&lt;/code&gt; suites test schema migrations and multi-event database isolation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Building a competition platform with zero dependencies taught me three core lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Constraints breed clarity:&lt;/strong&gt; When you can't install third-party libraries for every minor requirement, you stop over-engineering and focus on data flow and state transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security belongs in handlers, not CSS:&lt;/strong&gt; Hiding a button in HTML is not authorization. Every mutation must validate caller permissions, event scope, and timeline windows at the database/API boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability is peace of mind:&lt;/strong&gt; When results are published as frozen snapshots and reviews are append-only revisions, you eliminate an entire class of subtle data corruption bugs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;Lockdown is fully open source under the MIT License:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://lockdown-1-3tuu.onrender.com/" rel="noopener noreferrer"&gt;https://lockdown-1-3tuu.onrender.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📺 &lt;strong&gt;YouTube Walkthrough:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=ZfXDbT199II" rel="noopener noreferrer"&gt;Watch on YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Aryanxp1/Lockdown" rel="noopener noreferrer"&gt;https://github.com/Aryanxp1/Lockdown&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Tech Stack:&lt;/strong&gt; Python 3.12 (Standard Library), SQLite 3 (WAL mode), Vanilla CSS/JS, Docker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ZfXDbT199II" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To launch a complete competition instance with 48 seeded projects and demo accounts in under 10 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Aryanxp1/Lockdown.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Lockdown
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8081&lt;/code&gt; and explore!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks to the DOGFOOD 2026 team for an awesome competition challenge that gave me an excuse to build real infrastructure from first principles.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>sql</category>
      <category>python</category>
      <category>zerodependencies</category>
    </item>
    <item>
      <title>We Didn't Install the Package. We Built the Search Engine Instead.</title>
      <dc:creator>Aryan</dc:creator>
      <pubDate>Thu, 01 Oct 2026 10:37:44 +0000</pubDate>
      <link>https://dev.to/aryanxp1/we-didnt-install-the-package-we-built-the-search-engine-instead-27c8</link>
      <guid>https://dev.to/aryanxp1/we-didnt-install-the-package-we-built-the-search-engine-instead-27c8</guid>
      <description>&lt;h2&gt;
  
  
  The constraint
&lt;/h2&gt;

&lt;p&gt;What if you had to build a useful search engine, but you weren't allowed to install anything?&lt;/p&gt;

&lt;p&gt;That was the constraint I chose for the Zero Dependency Hackathon.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;FORGE&lt;/strong&gt;, a crash-safe local document search engine using only Python's standard library.&lt;/p&gt;

&lt;p&gt;No pip packages.&lt;br&gt;
No external runtime dependencies.&lt;br&gt;
No database library doing the heavy lifting.&lt;/p&gt;

&lt;p&gt;Just Python's standard library and a lot of code.&lt;/p&gt;
&lt;h2&gt;
  
  
  What normally happens
&lt;/h2&gt;

&lt;p&gt;If I wanted to build local document search quickly, I would probably reach for a search library such as Whoosh.&lt;/p&gt;

&lt;p&gt;The normal approach is straightforward:&lt;br&gt;
&lt;/p&gt;

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




Then &lt;span class="k"&gt;continue &lt;/span&gt;with the rest of your write-up.

&lt;span class="k"&gt;**&lt;/span&gt;Important:&lt;span class="k"&gt;**&lt;/span&gt; don&lt;span class="s1"&gt;'t paste the `# We Didn'&lt;/span&gt;t Install...&lt;span class="sb"&gt;`&lt;/span&gt; title as an H1 at the beginning. DEV already displays the title.

&lt;span class="nt"&gt;---&lt;/span&gt;

&lt;span class="c"&gt;### Step 5 — Add your screenshots&lt;/span&gt;

This is the part I&lt;span class="s1"&gt;'d definitely do because it makes the article much better.

When you reach a place where you want a screenshot:

1. Put your cursor there.
2. Press the **image icon** in the toolbar — the small picture icon.
3. Upload the screenshot.
4. DEV will insert it into the article.
5. Add a short sentence/caption around it if needed.

I'&lt;/span&gt;d use roughly &lt;span class="k"&gt;**&lt;/span&gt;3–4 screenshots&lt;span class="k"&gt;**&lt;/span&gt;, not 10+.

Good choices:

&lt;span class="k"&gt;**&lt;/span&gt;Screenshot 1 — FORGE GUI&lt;span class="k"&gt;**&lt;/span&gt;

Place after you introduce FORGE:

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;FORGE&lt;span class="s1"&gt;'s optional desktop interface for adding documents, searching, and checking index consistency.*

**Screenshot 2 — CLI search**

Place in the search/indexing section.

Show something like:

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
text&lt;br&gt;
python dist/forge.pyz search "python search" --or --ranked&lt;/p&gt;

&lt;h2&gt;
  
  
  See FORGE in action
&lt;/h2&gt;

&lt;p&gt;Here's the complete demo:&lt;/p&gt;

&lt;p&gt;[PASTE YOUR YOUTUBE LINK HERE]&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;The complete project is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Aryanxp1/forge" rel="noopener noreferrer"&gt;https://github.com/Aryanxp1/forge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository includes the source code, tests, documentation, reproducible build process, and demo dataset.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>programming</category>
      <category>devchallenge</category>
    </item>
  </channel>
</rss>
