<?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: Panagis Tzivras</title>
    <description>The latest articles on DEV Community by Panagis Tzivras (@ptzivras).</description>
    <link>https://dev.to/ptzivras</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%2F4080444%2F0ea7b5c3-877a-413e-a23c-d34f6c0a8e20.png</url>
      <title>DEV Community: Panagis Tzivras</title>
      <link>https://dev.to/ptzivras</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ptzivras"/>
    <language>en</language>
    <item>
      <title>The Map Works. Now Comes the Conversation.</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Tue, 10 Nov 2026 17:29:38 +0000</pubDate>
      <link>https://dev.to/ptzivras/-the-map-works-now-comes-the-conversation-4c17</link>
      <guid>https://dev.to/ptzivras/-the-map-works-now-comes-the-conversation-4c17</guid>
      <description>&lt;p&gt;&lt;em&gt;Preparing ARGOS : : for its first potential municipal presentation, and discovering that explaining software is another kind of engineering.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most of my work on ARGOS : : happens somewhere between a database, a terminal, and a map of Kefalonia.&lt;/p&gt;

&lt;p&gt;There is usually something concrete to investigate. A dataset has changed. A pipeline needs checking. A layer behaves differently than expected. Something that worked perfectly on my laptop has suddenly decided it hates phones.&lt;/p&gt;

&lt;p&gt;The usual routine.&lt;/p&gt;

&lt;p&gt;But recently, I found myself working on something quite different.&lt;/p&gt;

&lt;p&gt;A presentation.&lt;/p&gt;

&lt;p&gt;Not a new dataset. Not an API endpoint. Not another improvement to the map.&lt;/p&gt;

&lt;p&gt;Fifteen minutes to explain what I've been building to people who might actually have a use for it.&lt;/p&gt;

&lt;p&gt;And that introduced a surprisingly different set of engineering questions.&lt;/p&gt;

&lt;p&gt;What should I show first? How much background does somebody need? Which limitations belong in the demonstration? How do I make the map understandable without explaining the entire architecture behind it?&lt;/p&gt;

&lt;p&gt;And perhaps most importantly, how do I leave enough room to hear what the people across the table actually need?&lt;/p&gt;

&lt;p&gt;This is a build log about preparing for that conversation.&lt;/p&gt;

&lt;p&gt;The meeting itself is still pending.&lt;/p&gt;

&lt;h2&gt;
  
  
  From building features to explaining decisions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://argos-geo.org" rel="noopener noreferrer"&gt;ARGOS : :&lt;/a&gt; is my independent, open-source geospatial initiative for Kefalonia, Greece.&lt;/p&gt;

&lt;p&gt;It brings together geographic datasets, environmental information, and documented spatial analyses in an interactive map. The idea is to make information about places, infrastructure, and changing conditions easier to explore, question, and understand.&lt;/p&gt;

&lt;p&gt;I build it alongside my full-time data engineering job.&lt;/p&gt;

&lt;p&gt;Time is therefore not just a personal constraint. It has become something of an architectural principle. 😄&lt;/p&gt;

&lt;p&gt;For the potential municipal presentation, I've prepared roughly fifteen minutes of material.&lt;/p&gt;

&lt;p&gt;Enough to introduce the platform, demonstrate a couple of working examples, and explain why the underlying information matters.&lt;/p&gt;

&lt;p&gt;Not enough to show everything.&lt;/p&gt;

&lt;p&gt;And that turned out to be a useful restriction.&lt;/p&gt;

&lt;p&gt;When you're building software, it is tempting to show how much you've built. Every layer represents work. Every analysis has a story. Every feature has a reason for existing.&lt;/p&gt;

&lt;p&gt;But an audience doesn't need to see your development backlog.&lt;/p&gt;

&lt;p&gt;They need to understand what they're looking at.&lt;/p&gt;

&lt;p&gt;So I started asking a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What should someone understand after seeing this example?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A flood-susceptibility layer can start a conversation about terrain, water accumulation, and the limitations of geographic screening.&lt;/p&gt;

&lt;p&gt;A fire-service accessibility analysis can show how a road network, estimated travel speeds, and documented assumptions produce an estimate of accessibility.&lt;/p&gt;

&lt;p&gt;Neither requires a tour of the database.&lt;/p&gt;

&lt;p&gt;Both require enough context for someone to understand what the result does and does not mean.&lt;/p&gt;

&lt;p&gt;The challenge isn't demonstrating that the platform contains information.&lt;/p&gt;

&lt;p&gt;It's demonstrating how that information can be interpreted responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first conversation widened the picture
&lt;/h2&gt;

&lt;p&gt;I contacted the Municipality of Argostoli to introduce the project and eventually had an encouraging telephone conversation with a municipal contact.&lt;/p&gt;

&lt;p&gt;We discussed potential applications, including geographically relevant visitor information accessible through QR codes and the mapping of emergency assembly points.&lt;/p&gt;

&lt;p&gt;He also mentioned that he would explore whether other municipal colleagues might be interested in seeing the platform.&lt;/p&gt;

&lt;p&gt;Nothing formal was agreed. No pilot, partnership, or development commitment. The possibility of a presentation remains open.&lt;/p&gt;

&lt;p&gt;But the conversation was useful even without a meeting date.&lt;/p&gt;

&lt;p&gt;Until then, much of my preparation had naturally centred on environmental and Civil Protection examples.&lt;/p&gt;

&lt;p&gt;Flood susceptibility. Wildfire screening. Accessibility. Changing conditions.&lt;/p&gt;

&lt;p&gt;Those are important applications, but they're not the whole purpose of a digital twin.&lt;/p&gt;

&lt;p&gt;A geographic platform can potentially support many different questions. Infrastructure, public information, municipal assets, environmental conditions, accessibility, and how places relate to one another.&lt;/p&gt;

&lt;p&gt;Different departments see the same island through different responsibilities.&lt;/p&gt;

&lt;p&gt;That made me reconsider what I wanted from the presentation.&lt;/p&gt;

&lt;p&gt;Rather than arriving with a list of features and asking which ones people liked, I wanted to understand something more fundamental:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does geographic information become difficult to find, maintain, combine, or explain in someone's everyday work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a question I cannot answer from my terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitations belong in the demonstration
&lt;/h2&gt;

&lt;p&gt;One of the principles behind ARGOS : : is that the map should not simply display a result and expect people to trust it.&lt;/p&gt;

&lt;p&gt;Sources matter.&lt;/p&gt;

&lt;p&gt;Methods matter.&lt;/p&gt;

&lt;p&gt;Assumptions matter.&lt;/p&gt;

&lt;p&gt;And limitations matter just as much.&lt;/p&gt;

&lt;p&gt;Preparing the presentation made me think about how those details should appear while someone is looking at the geographic result.&lt;/p&gt;

&lt;p&gt;Consider three examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flood susceptibility:&lt;/strong&gt; A terrain-based screening layer can highlight places where runoff may concentrate or water may accumulate. It does not predict when flooding will occur, how deep the water will be, or whether local drainage infrastructure will prevent it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fire-service accessibility:&lt;/strong&gt; A network analysis can estimate travel times using road connections and assumed speeds. It cannot establish actual emergency response times without operational observations and further validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marine conditions:&lt;/strong&gt; A modelled field can describe predicted waves or currents at a particular resolution and valid time. That doesn't make it a direct local observation or a guarantee of conditions at a specific beach.&lt;/p&gt;

&lt;p&gt;Those aren't footnotes.&lt;/p&gt;

&lt;p&gt;They change how the information should be interpreted.&lt;/p&gt;

&lt;p&gt;The map already provides a &lt;em&gt;“How do we know?”&lt;/em&gt; route to explanatory information associated with its analytical layers. That gives me a way to bring sources and limitations into the demonstration rather than relegating them to a methodology document nobody has time to open.&lt;/p&gt;

&lt;p&gt;Some of the analyses also have documented misses.&lt;/p&gt;

&lt;p&gt;I think those deserve a place in the project record alongside successful checks. A model that gets something wrong can be informative, provided the failure is investigated rather than hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A useful map should make uncertainty easier to investigate, not easier to overlook.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There will inevitably be questions I cannot answer during a presentation.&lt;/p&gt;

&lt;p&gt;Sometimes the answer will be that the existing data is insufficient.&lt;/p&gt;

&lt;p&gt;Sometimes it will be that a result has been internally checked but not independently validated against real-world observations.&lt;/p&gt;

&lt;p&gt;And sometimes local experience may reveal something the model simply doesn't know.&lt;/p&gt;

&lt;p&gt;That's not necessarily a failed demonstration.&lt;/p&gt;

&lt;p&gt;It might be the beginning of a useful technical conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo is another kind of test
&lt;/h2&gt;

&lt;p&gt;I've spent some time rehearsing the presentation.&lt;/p&gt;

&lt;p&gt;Not just the words, but the software.&lt;/p&gt;

&lt;p&gt;Opening a geographic deep link.&lt;/p&gt;

&lt;p&gt;Checking that the intended layer appears.&lt;/p&gt;

&lt;p&gt;Making sure the legend is visible.&lt;/p&gt;

&lt;p&gt;Opening the explanatory information.&lt;/p&gt;

&lt;p&gt;Closing a panel without losing the geographic context.&lt;/p&gt;

&lt;p&gt;Moving between a slide and the live map.&lt;/p&gt;

&lt;p&gt;These sound like trivial interactions.&lt;/p&gt;

&lt;p&gt;When you're developing alone, they usually are.&lt;/p&gt;

&lt;p&gt;But in a fifteen-minute presentation, every unnecessary click steals attention from the actual subject.&lt;/p&gt;

&lt;p&gt;And I discovered something interesting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A feature can be technically functional without the workflow around it being ready to demonstrate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A layer might render correctly. Its data may be available. Its information panel may work.&lt;/p&gt;

&lt;p&gt;But if reaching the explanation requires several awkward interactions, the audience can lose the thread long before you reach the interesting part.&lt;/p&gt;

&lt;p&gt;That means rehearsing a demonstration is also a form of interface testing.&lt;/p&gt;

&lt;p&gt;Not a replacement for automated tests, data checks, or proper usability testing.&lt;/p&gt;

&lt;p&gt;A different perspective.&lt;/p&gt;

&lt;p&gt;You're no longer asking only whether the component works.&lt;/p&gt;

&lt;p&gt;You're asking whether a person unfamiliar with the project can follow what is happening.&lt;/p&gt;

&lt;p&gt;For me, that distinction has already been useful.&lt;/p&gt;

&lt;p&gt;It has also influenced the ongoing visual design work around ARGOS : :. Giving the geography more room, making evidence easier to reach, and reducing unnecessary interface complexity are not merely aesthetic decisions.&lt;/p&gt;

&lt;p&gt;They affect how easily someone can understand the map.&lt;/p&gt;

&lt;p&gt;And understanding is rather important when the software is trying to communicate geographic evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  From technical assumptions to local knowledge
&lt;/h2&gt;

&lt;p&gt;Building ARGOS : : has required making countless decisions about what information to include and which questions might be useful.&lt;/p&gt;

&lt;p&gt;Some of those decisions came from available datasets.&lt;/p&gt;

&lt;p&gt;Others came from research, engineering constraints, and my own familiarity with Kefalonia.&lt;/p&gt;

&lt;p&gt;But knowing an island and understanding how its public services work are not the same thing.&lt;/p&gt;

&lt;p&gt;A municipal employee may know about a road that regularly becomes inaccessible, a location that floods despite appearing unremarkable in a terrain model, or an important dataset that exists only in an internal spreadsheet.&lt;/p&gt;

&lt;p&gt;That knowledge doesn't automatically appear in OpenStreetMap, Copernicus products, or a PostGIS database.&lt;/p&gt;

&lt;p&gt;And it shouldn't be invented to fill the gap.&lt;/p&gt;

&lt;p&gt;An actual conversation could help identify where the published geographic picture agrees with local experience, where it doesn't, and which missing information would genuinely improve an analysis.&lt;/p&gt;

&lt;p&gt;That is one reason I'm interested in the potential meeting.&lt;/p&gt;

&lt;p&gt;Not because I expect someone to validate everything I've built.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;I want to find out which assumptions survive contact with people who deal with the island's practical problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the outreach stands
&lt;/h2&gt;

&lt;p&gt;After the initial telephone conversation, I sent a follow-up email with my availability.&lt;/p&gt;

&lt;p&gt;As I write this, I haven't received a response, and no meeting has been scheduled.&lt;/p&gt;

&lt;p&gt;I don't know the reason, and I don't think there's much value in guessing.&lt;/p&gt;

&lt;p&gt;Municipal schedules are their own world, and the discussion was exploratory from the beginning.&lt;/p&gt;

&lt;p&gt;My full-time job also has some substantial deadlines at the moment, so I'll follow up when I have enough room to give the conversation the attention it deserves.&lt;/p&gt;

&lt;p&gt;For now, ARGOS : : remains publicly accessible, the presentation material is prepared, and the possibility of a meeting remains open.&lt;/p&gt;

&lt;p&gt;There is no launch date, partnership announcement, or pilot waiting behind this article.&lt;/p&gt;

&lt;p&gt;Just a working project and a conversation I'd like to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I hope to learn
&lt;/h2&gt;

&lt;p&gt;If the meeting happens, my objective is fairly simple.&lt;/p&gt;

&lt;p&gt;Show what works today.&lt;/p&gt;

&lt;p&gt;Explain what doesn't.&lt;/p&gt;

&lt;p&gt;And listen.&lt;/p&gt;

&lt;p&gt;I'd like to understand which geographic questions municipal teams regularly encounter, what information they already maintain, and which gaps might be worth investigating.&lt;/p&gt;

&lt;p&gt;I'd also like to hear the technical objections.&lt;/p&gt;

&lt;p&gt;Who maintains the data?&lt;/p&gt;

&lt;p&gt;How frequently does it change?&lt;/p&gt;

&lt;p&gt;What happens when a source is unavailable?&lt;/p&gt;

&lt;p&gt;How do we distinguish an estimate from an observation?&lt;/p&gt;

&lt;p&gt;Which results are useful for exploration, and which would need substantially more validation before supporting an operational decision?&lt;/p&gt;

&lt;p&gt;These are questions worth answering whether or not any formal collaboration follows.&lt;/p&gt;

&lt;p&gt;Preparing for this potential meeting has already made me look at ARGOS : : differently.&lt;/p&gt;

&lt;p&gt;For months, the central challenge has been getting the pieces to work together: data pipelines, spatial processing, APIs, map layers, evidence, and infrastructure.&lt;/p&gt;

&lt;p&gt;Now I'm thinking more about what happens when the person looking at the map wasn't involved in building any of those pieces.&lt;/p&gt;

&lt;p&gt;Can they understand what it shows?&lt;/p&gt;

&lt;p&gt;Can they find out where the information came from?&lt;/p&gt;

&lt;p&gt;Can they question the result?&lt;/p&gt;

&lt;p&gt;Can they tell me something about the geography that the software missed?&lt;/p&gt;

&lt;p&gt;I suspect those questions will become increasingly important as the project develops.&lt;/p&gt;

&lt;p&gt;The map is ready to open.&lt;/p&gt;

&lt;p&gt;I'm interested in what they'll ask when it does.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Explore the project:&lt;/strong&gt; &lt;a href="https://argos-geo.org" rel="noopener noreferrer"&gt;ARGOS : :&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Open the working map:&lt;/strong&gt; &lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;argos-geo.org/map&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/argos-geo/kefalonia-digital-twin" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you've brought an independent technical project to a local authority, what helped make that first conversation useful?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>gis</category>
      <category>devlog</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Cable: How a €12 adapter turned a laptop into infrastructure</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Sun, 25 Oct 2026 13:10:36 +0000</pubDate>
      <link>https://dev.to/ptzivras/the-cable-how-a-eu12-adapter-turned-a-laptop-into-infrastructure-7h4</link>
      <guid>https://dev.to/ptzivras/the-cable-how-a-eu12-adapter-turned-a-laptop-into-infrastructure-7h4</guid>
      <description>&lt;h2&gt;
  
  
  &lt;em&gt;Frankie was alive. But Frankie was not yet a server. This is the story of the cable that changed the verdict.&lt;/em&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The adapter arrived on a Tuesday
&lt;/h2&gt;

&lt;p&gt;I had ordered it from a local electronics shop in Argostoli. USB 3.0 to Ethernet. Realtek chipset. The clerk did not ask what it was for. I did not explain that a 2008 MacBook Air was waiting for it.&lt;/p&gt;

&lt;p&gt;I powered Frankie down, plugged the adapter into the USB port, ran an Ethernet cable to the router, and powered back on.&lt;/p&gt;

&lt;p&gt;The kernel saw it immediately. A new interface appeared. DHCP fired. I opened a terminal.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ping -c 100 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One hundred packets sent. One hundred received. Zero percent loss. Round-trip times under two milliseconds. The same router that had given Frankie nine percent loss over WiFi now gave perfect reliability over copper.&lt;/p&gt;

&lt;p&gt;The b43 driver was no longer in the critical path.&lt;/p&gt;

&lt;p&gt;I configured a static DHCP reservation on the router so Frankie would always get the same local address. Then I tested the one thing that mattered.&lt;/p&gt;

&lt;p&gt;I ran the backup script.&lt;/p&gt;

&lt;p&gt;It SSHed into the Oracle VM. It checked disk space. It checked load average. It streamed a pg_dump over the encrypted tunnel. Four minutes later, a 132 MB file sat on Frankie's disk, verified with pg_restore, containing every table, every spatial index, every road segment and building footprint in the ARGOS : : twin.&lt;/p&gt;

&lt;p&gt;I ran it again. It passed. I ran it a third time. It passed. I deleted the test files and ran it a fourth time, this time with the full cron environment exported, mimicking the minimal shell that cron provides at 04:00.&lt;/p&gt;

&lt;p&gt;It passed.&lt;/p&gt;

&lt;p&gt;The wall was down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless
&lt;/h2&gt;

&lt;p&gt;With a reliable network path, Frankie could finally become what it was meant to be. I changed the default boot target from graphical to multi-user. Cold boot. The screen showed a text login prompt. No LXQt desktop. No wallpaper. No mouse cursor.&lt;/p&gt;

&lt;p&gt;I closed the lid. The machine kept running. No sleep. No suspend. No lit panel generating heat in a closed chassis. The machine stands vertically on the table, exhaust side down, so the fan can pull air through without cooking the chips. mbpfan manages speed based on temperature sensor readings. Core temperatures stayed in the sixties. The NVIDIA 9400M GPU idled without a display server asking it to render anything.&lt;/p&gt;

&lt;p&gt;From the Windows laptop across the room, I opened an SSH session. All three services responded. Then I opened X2Go. The remote desktop connected. X2Go creates its own session. It does not need a local GUI to exist. Frankie was headless and accessible at the same time.&lt;/p&gt;

&lt;p&gt;Reversibility is preserved. If I need a local GUI for maintenance, one command brings it back without changing the default. If something goes seriously wrong, GRUB recovery mode leads to a root shell. The transformation is not a prison. It is a preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The services
&lt;/h2&gt;

&lt;p&gt;Three things now run on Frankie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File sync.&lt;/strong&gt; Syncthing bridges Frankie and the development laptop. I tested it with a project image, synced it across, then deleted it on the source. The file survived in a versioned archive with a timestamp suffix. Sync proven. Recovery proven. The bridge is solid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git mirror.&lt;/strong&gt; Gitea runs under a dedicated system user, backed by SQLite. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The backup.&lt;/strong&gt; The script runs daily at 04:00. It connects to the cloud VM, streams the full PostGIS database, writes to a temporary file, verifies the dump with a restore listing, and only then atomically promotes it to a final archive. The previous good dump survives until the replacement is proven. Four manual test runs passed. The first unattended cron run is the real examination.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure, right now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Local dev: Frankie, 2008 MacBook Air, Lubuntu, headless, USB Ethernet, mbpfan tuned to 55/60&lt;/li&gt;
&lt;li&gt;File sync: Syncthing, versioned, tested&lt;/li&gt;
&lt;li&gt;Git mirror: Gitea&lt;/li&gt;
&lt;li&gt;Backup: Daily pg_dump from Oracle VM, atomic promotion, four test runs passed&lt;/li&gt;
&lt;li&gt;Production database: Oracle Cloud VM, PostGIS 16, pgRouting&lt;/li&gt;
&lt;li&gt;Site: Cloudflare Pages, global CDN&lt;/li&gt;
&lt;li&gt;Tiles: GitHub Pages, PMTiles&lt;/li&gt;
&lt;li&gt;Builds: GitHub Codespaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no procurement department. There is no EU grant. There is one person, a vertical laptop on a table, and a €12 cable that turned a waiting machine into a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;p&gt;Frankie was never going to be fast. A Core 2 Duo with under 2 GB of usable RAM will not run Docker. It will not compile a spatial index. It will not serve map tiles to a thousand users.&lt;/p&gt;

&lt;p&gt;But Frankie does not need to be fast. Frankie needs to be reliable. And reliability, on this hardware, is a design problem, not a purchasing problem.&lt;/p&gt;

&lt;p&gt;The b43 WiFi ceiling forced the Ethernet adapter. The slow CPU forced lightweight services. The limited RAM forced careful process management. Each constraint removed an option and sharpened the design.&lt;/p&gt;

&lt;p&gt;Six months ago, this machine was headed for recycling. The WiFi was broken. The trackpad was erratic. The battery held minutes. The screen had lines.&lt;/p&gt;

&lt;p&gt;Today it stands vertically on a table next to the router, lid closed, running three services, safeguarding a database, mirroring a repository, and syncing files across devices. It will never be fast. It does not need to be. It needs to do one job, reliably, while the island sleeps.&lt;/p&gt;

&lt;p&gt;The backup script runs tonight at 04:00. We will know tomorrow morning if the safeguard actually safeguards.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Frankie is alive. Frankie is a server. And if you walk past that table at four in the morning, the only light you will see is the Ethernet port blinking next to the router.&lt;/strong&gt; 🤖&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>backup</category>
    </item>
    <item>
      <title>The Wall: How a Wi-Fi driver killed the server dream</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:36:47 +0000</pubDate>
      <link>https://dev.to/ptzivras/the-wall-how-a-wi-fi-driver-killed-the-server-dream-4e2f</link>
      <guid>https://dev.to/ptzivras/the-wall-how-a-wi-fi-driver-killed-the-server-dream-4e2f</guid>
      <description>&lt;p&gt;Eight percent packet loss, a kernel panic, and the €15 answer that was hiding in plain sight&lt;/p&gt;




&lt;p&gt;The morning after Frankie came alive, I wrote a backup script.&lt;/p&gt;

&lt;p&gt;A hundred lines of bash that would SSH into the Oracle VM, check that PostgreSQL was healthy, check that disk space was above 5 GB, check that load was below 4.0. If everything passed, it would pull a &lt;code&gt;pg_dump&lt;/code&gt;, gzip it, and store it on Frankie's 120 GB disk. Two dumps kept. Everything older deleted. A safeguard, not an archive. Geographic redundancy: the VM in Frankfurt, Frankie on Kefalonia. The job would run at 04:00, when the island sleeps and the WiFi air is quiet.&lt;/p&gt;

&lt;p&gt;I finished the script at 23:00. I ran one test before bed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ping -c 100 8.8.8.8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sixty percent loss.&lt;/p&gt;

&lt;p&gt;That was from across the room. I moved Frankie next to the router. Eighty-eight percent signal. Two hundred seventy megabits per second negotiated rate. Locked to 2.4 GHz, powersave disabled.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ping -c 100 8.8.8.8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nine percent loss.&lt;/p&gt;

&lt;p&gt;The b43 driver, the open-source driver for the Broadcom BCM4321, has a hard ceiling. I spent the next three hours proving it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ceiling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The internet is full of tuning advice for b43. Disable powersave. Lock to 2.4 GHz. Force legacy rates with &lt;code&gt;iw&lt;/code&gt;. Add module options. I did all of it.&lt;/p&gt;

&lt;p&gt;I disabled powersave in NetworkManager. I locked the connection to 802.11b/g with &lt;code&gt;nmcli&lt;/code&gt;. I installed &lt;code&gt;iw&lt;/code&gt; and forced legacy-2.4 bitrates. I moved the machine until it was touching the router. The signal indicator showed full bars. The negotiated rate showed 270 Mbit/s, a figure that is impossible on 2.4 GHz channel 1 with 20 MHz width, which means the router was using 40 MHz channel bonding, a mode b43 handles badly.&lt;/p&gt;

&lt;p&gt;I forced 20 MHz width. I forced lower rates. I added &lt;code&gt;options b43 nohwcrypt=1 qos=0&lt;/code&gt; to &lt;code&gt;/etc/modprobe.d/b43.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ping -c 100 8.8.8.8&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nine percent loss. Round-trip times spiking to 86 milliseconds. The driver was not failing because of configuration. The driver was failing because it is the driver. The b43 codebase for the BCM4321 rev 05 has a ceiling, and that ceiling is 8 to 10 percent packet loss at full signal.&lt;/p&gt;

&lt;p&gt;This is not a signal problem. This is a driver problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The escape hatch that was not&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every forum thread about the BCM4321 ends the same way. "Just use the proprietary wl driver." I tried.&lt;/p&gt;

&lt;p&gt;I installed &lt;code&gt;bcmwl-kernel-source&lt;/code&gt;. I blacklisted b43, bcma, ssb. I rebooted.&lt;/p&gt;

&lt;p&gt;The kernel panicked. &lt;code&gt;dmesg&lt;/code&gt; showed &lt;code&gt;wl_module_init&lt;/code&gt; call stacks. The wl driver crashes on init with this kernel, on this hardware, after the 193-package upgrade that fixed everything else. The upgrade that brought b43 to life did not make wl viable. They are mutually exclusive on this machine, and wl loses.&lt;/p&gt;

&lt;p&gt;I reverted. Removed the blacklist, reloaded b43, reconnected. Nine percent loss. But at least it connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What nine percent means&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nine percent packet loss does not matter for browsing. Falkon retries. Pages load. It does not matter for SSH: type a command, if a packet drops, the terminal hangs for half a second, then recovers. It does not matter for writing this article in Geany, which is entirely offline.&lt;/p&gt;

&lt;p&gt;But nine percent matters for an unattended 04:00 backup job pulling a multi-gigabyte PostgreSQL dump over SSH. A dropped packet in the middle of an &lt;code&gt;scp&lt;/code&gt; does not recover cleanly. The transfer hangs. The cron job fails. The safeguard is not safe. A backup that fails silently is worse than no backup, because it gives you false confidence.&lt;/p&gt;

&lt;p&gt;I tested this. I ran the script manually. It failed twice in three attempts. The third attempt completed, but the transfer took fourteen minutes for a 1.2 GB dump because TCP was constantly retransmitting. Over a wired connection, the same transfer would take four minutes.&lt;/p&gt;

&lt;p&gt;Frankie could not be a server over WiFi. Not because the WiFi was weak, but because the driver was honest about its limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The €15 answer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A USB Ethernet adapter costs €15. Any USB 2.0 to RJ45 adapter works on Linux without drivers. Plug it in, the kernel sees &lt;code&gt;eth0&lt;/code&gt;, DHCP fires, ping shows zero percent loss. The backup script works. The cron job works. Frankie becomes the server it was meant to be.&lt;/p&gt;

&lt;p&gt;I do not have the adapter yet. On the island, it might take a week to find one. Until then, Frankie is in limbo. The script is written. The cron is ready. The machine waits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pivot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If Frankie cannot come to the work over WiFi, and the adapter is not here yet, then Frankie's role must change. I stripped the server plan. No headless conversion. No 04:00 cron. No Tailscale tunnel. Frankie stays in the GUI, running LXQt, waiting.&lt;/p&gt;

&lt;p&gt;The options without the adapter are real but limited. Frankie can be a learning sandbox: break it, reinstall it, try Arch, try Ansible, try Docker. The WiFi flakiness does not matter because the work is local. Frankie can be a distraction-free writing machine: Geany, Vim, Markdown, no browser, no notifications. Frankie can babysit an Arduino or an ESP32 over USB. The machine does not need internet; it needs a serial port and a screen.&lt;/p&gt;

&lt;p&gt;But none of these are what Frankie was meant to be. Frankie was meant to be a server. A backup anchor. A quiet machine in the corner that pulls a dump while I sleep.&lt;/p&gt;

&lt;p&gt;That future costs €15. Not €1,500 for a new laptop. Not €500 for a used ThinkPad. €15 for a USB Ethernet adapter that removes the wireless variable entirely.&lt;/p&gt;

&lt;p&gt;Sometimes the cheapest solution is the one you pay for.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The infrastructure, right now:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local dev:&lt;/strong&gt; Frankie, 2008 MacBook Air, Lubuntu, LXQt, no GPU acceleration, &lt;code&gt;b43&lt;/code&gt; WiFi with 9% loss, &lt;code&gt;mbpfan&lt;/code&gt; tuned to 55/60&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production database:&lt;/strong&gt; Oracle Cloud Always Free ARM VM, PostGIS 16, pgRouting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; Cloudflare Pages, global CDN&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tiles:&lt;/strong&gt; GitHub Pages, PMTiles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Builds:&lt;/strong&gt; GitHub Codespaces&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt; Resend SMTP, daily VM health reports at 08:00 EEST&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The backup script:&lt;/strong&gt; Written, tested, failing. Waiting for a cable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no procurement department. There is no EU grant. There is one person, a drawer of old hardware, and a shopping list with one item on it.&lt;/p&gt;

&lt;p&gt;Frankie is alive. But Frankie is not yet a server.&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>opensource</category>
      <category>database</category>
      <category>network</category>
    </item>
    <item>
      <title>ARGOS : : The map works on phones now</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:42:24 +0000</pubDate>
      <link>https://dev.to/ptzivras/argos-the-map-works-on-phones-now-3mop</link>
      <guid>https://dev.to/ptzivras/argos-the-map-works-on-phones-now-3mop</guid>
      <description>&lt;p&gt;ARGOS : : started on a 27-inch monitor. Wide screen, mouse, keyboard, time to think. That is how you build a digital twin. It is not how you use one.&lt;/p&gt;

&lt;p&gt;Civil protection happens in trucks, on beaches, at fire posts, in the rain. The person who needs the map is holding a phone, possibly with gloves, possibly in a hurry, possibly without a desk in sight. If the map does not work on a phone, it does not work where it matters.&lt;/p&gt;

&lt;p&gt;v0.0.8 is the first mobile-focused release. Not a redesign. A repair of the worst friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;44 px touch targets.&lt;/strong&gt; The layer toggles, the zoom buttons, the legend items. Every interactive element is now at least 44 px in both directions. No more fat-finger misses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layer drawer auto-dismiss.&lt;/strong&gt; Tap the map, the drawer slides away. The map is the focus; the UI gets out of the way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marine slider no longer buried.&lt;/strong&gt; The wave height slider was hiding under the legend on small screens. Now it sits above it, reachable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bilingual gold toast.&lt;/strong&gt; Toggle a layer, and a small gold banner confirms: "Ενεργοποιήθηκε" in Greek, "Enabled" in English. No ambiguity about whether your tap registered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Popup sizing.&lt;/strong&gt; Building popups now fit within a 375 px viewport. No horizontal scroll. No clipped text.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What did not change:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The models are the same. The data is the same. The validation is the same. This is a usability pass, not a content update. The map is still a screening baseline with directional validation, not an operational forecast. The marine layer is still unvalidated. The Ithaca ferry is still not modelled.&lt;/p&gt;

&lt;p&gt;But the map is now usable on the device you actually carry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test it.&lt;/strong&gt; Open argos-geo.org/map on your phone. Toggle the wildfire layer. Tap a building. Ask the twin a question. It works. It is not perfect. It is better than last week. And next week it will be better again.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>mobile</category>
      <category>geospatial</category>
      <category>data</category>
    </item>
    <item>
      <title>Meet Frankie, the 17-year-old laptop that was supposed to build ARGOS : :</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:05:24 +0000</pubDate>
      <link>https://dev.to/ptzivras/meet-frankie-the-17-year-old-laptop-that-was-supposed-to-build-argos-o2f</link>
      <guid>https://dev.to/ptzivras/meet-frankie-the-17-year-old-laptop-that-was-supposed-to-build-argos-o2f</guid>
      <description>&lt;p&gt;A 2008 MacBook Air, six hours of Linux archaeology, and the belief that one person with a drawer of old hardware can still matter&lt;/p&gt;




&lt;p&gt;I am writing this from a 2008 MacBook Air that macOS abandoned a decade ago. Its name is Frankie. It took six hours to bring it to life, and for one evening, I believed it would be the machine that kept ARGOS running while I am on Kefalonia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ARGOS : : is a digital twin for an island, built by one person. There is no procurement department. There is no EU grant. There is one person and a drawer of old hardware.&lt;/p&gt;

&lt;p&gt;The production database runs on an Oracle Cloud VM. The site runs on Cloudflare Pages. The tiles run on GitHub Pages. The heavy builds run in GitHub Codespaces. Every piece of infrastructure was chosen because it does the job without requiring a team to maintain it. The stack is small because the team is one person. The stack is public because the methodology should be auditable. That is the design, not a compromise.&lt;/p&gt;

&lt;p&gt;But I still need a local machine. For the Git commits. For the YAML edits. For the SSH into the VM when something breaks at 23:00 and the Codespace has gone cold. For the fieldwork notes I write on the balcony after walking a ravine.&lt;/p&gt;

&lt;p&gt;I did not buy a new laptop. I pulled the MacBook Air 2,1 out of a drawer. The one with the Nvidia 9400M chip that Apple stopped supporting somewhere around Mountain Lion. The one that modern macOS will not even boot on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The archaeology&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lubuntu 24.04.5 LTS. That was the starting point. Lightweight, supported, sane.&lt;/p&gt;

&lt;p&gt;The first attempt failed. The Calamares installer hit a known bug :&lt;code&gt;fixconkeys-part1&lt;/code&gt; timed out after 30 seconds, leaving the disk half-erased and the boot chain corrupted. I rebooted into a grey box with an inverted flag. The fix, found in a forum post from 2023, was to edit &lt;code&gt;/etc/calamares/settings.conf&lt;/code&gt; in the live session, comment out two lines, and run the installer again. It worked the second time, though the installer window went black mid-process and I had to trust the fan noise.&lt;/p&gt;

&lt;p&gt;Then the display. The live session booted fine, but the installed system showed corrupted text — white boxes where characters should be, inverted colors, blank desktop icons. &lt;code&gt;nomodeset&lt;/code&gt; fixed the text but turned the sky brown and the sea purple. The real fix was &lt;code&gt;nouveau.noaccel=1&lt;/code&gt;: disable GPU acceleration entirely, tell the kernel to stop asking the 9400M for help. The CPU draws every pixel. No 3D. No hardware compositing. Just a Core 2 Duo doing graphics the hard way, like it is 2006.&lt;/p&gt;

&lt;p&gt;And it worked. Colors correct. Icons visible. Desktop stable. The trade-off: no 3D acceleration, which is irrelevant for a machine that was never going to run a game.&lt;/p&gt;

&lt;p&gt;Then the WiFi. The Broadcom BCM4321 chip is a known problem child. The proprietary &lt;code&gt;wl&lt;/code&gt; driver loads but crashes on this kernel. The open-source &lt;code&gt;b43&lt;/code&gt; driver works, but the signal indicator always shows full bars, whether you are next to the router or three rooms away. The connection is real; the bars are fiction. I can live with that.&lt;/p&gt;

&lt;p&gt;The trackpad took three attempts. The first driver moved the cursor but would not click. The second clicked but had no right-click. The third, a manual &lt;code&gt;xinput&lt;/code&gt; configuration, gave me both.&lt;/p&gt;

&lt;p&gt;Then the heat. The default fan control waits until 63°C before it starts ramping. By then the palm rest is already hot. I installed &lt;code&gt;mbpfan&lt;/code&gt; and tuned the curve: &lt;code&gt;low_temp = 55&lt;/code&gt;, &lt;code&gt;high_temp = 60&lt;/code&gt;, &lt;code&gt;max_temp = 86&lt;/code&gt;, &lt;code&gt;polling_interval = 1&lt;/code&gt;. The fan stays silent below 55°C, starts a gentle ramp at 55 instead of waiting for 63, and never hits full speed unless the CPU is at 86. A cooler machine with slightly more fan noise. I chose usable.&lt;/p&gt;

&lt;p&gt;Then the suspend. The MacBookAir2,1 has a documented kernel quirk: close the lid, and sometimes it wakes to a black screen that never clears. I call it a design feature that forces me to shut down properly. If I step away, I save and power off.&lt;/p&gt;

&lt;p&gt;Six hours. From a forgotten drawer to a working desktop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first evening&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I installed Geany, a lightweight editor that opens in under a second. Falkon, a Qt browser that loads pages without choking. I disabled the screensaver entirely, it was rendering while idle, heating the machine for no reason. I set the monitor to switch off after six minutes.&lt;/p&gt;

&lt;p&gt;I wrote the first draft of this article on Frankie itself. The text appeared as I typed. The fan hummed quietly. The palm rest was warm but not hot. For one evening, it felt like the plan would work.&lt;/p&gt;

&lt;p&gt;Frankie was alive.&lt;/p&gt;

&lt;p&gt;But alive and useful are different things. The next morning, I would learn what a 2 GB Core 2 Duo actually feels like in 2026.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The infrastructure, at that moment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local dev:&lt;/strong&gt; Frankie, 2008 MacBook Air, Lubuntu, no GPU acceleration, &lt;code&gt;b43&lt;/code&gt; WiFi, manual trackpad config, &lt;code&gt;mbpfan&lt;/code&gt; tuned to 55/60&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production database:&lt;/strong&gt; Oracle Cloud Always Free ARM VM, PostGIS 16, pgRouting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; Cloudflare Pages, global CDN&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tiles:&lt;/strong&gt; GitHub Pages, PMTiles, 10.88 MB vector tileset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Builds:&lt;/strong&gt; GitHub Codespaces, ephemeral, rebuilt on demand&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt; Resend SMTP, daily VM health reports at 08:00 EEST&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; MinIO on the VM, 30-day backup retention&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Domain cost:&lt;/strong&gt; €1 per month. The rest is built, not bought.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The big twins , Volos, DestinE, HARMONIA Piraeus, run on HPC clusters and EU budgets and consultant teams. ARGOS : : runs on one person, a free VM, and a drawer of old hardware.&lt;/p&gt;

&lt;p&gt;That is not a limitation. That is the point.&lt;/p&gt;

&lt;p&gt;Or so I thought.&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>opensource</category>
      <category>database</category>
      <category>performance</category>
    </item>
    <item>
      <title>The Sea Looks Calm When It Isn't</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Thu, 01 Oct 2026 06:06:04 +0000</pubDate>
      <link>https://dev.to/ptzivras/the-sea-looks-calm-when-it-isnt-3e6m</link>
      <guid>https://dev.to/ptzivras/the-sea-looks-calm-when-it-isnt-3e6m</guid>
      <description>&lt;p&gt;I spent years reading water before I ever wrote a line of code. So when I say this, it comes from both sides of me: the sea on a map and the sea in front of you are not the same object.&lt;/p&gt;

&lt;p&gt;Myrtos Beach is one of the island's best-known beaches. White pebbles, steep cliffs, water that shifts from turquoise to deep blue in a few meters. On most summer days it looks like the safest place on Kefalonia. It is not always.&lt;/p&gt;

&lt;p&gt;This is the story of the first marine layer in ARGOS, and of the honest limits of what a model can promise you about the sea.&lt;/p&gt;

&lt;h2&gt;
  
  
  The night it earned its keep
&lt;/h2&gt;

&lt;p&gt;On the night the marine layer went live, the model logged an exposure score of &lt;strong&gt;73.3&lt;/strong&gt; for Myrtos. Class: &lt;strong&gt;high&lt;/strong&gt;. Window: &lt;strong&gt;22:00 to 23:00 EEST&lt;/strong&gt;. Waves from &lt;strong&gt;316 degrees&lt;/strong&gt;, about &lt;strong&gt;2 m&lt;/strong&gt; significant wave height.&lt;/p&gt;

&lt;p&gt;Nothing about that sentence is measured at the shoreline. It is computed from Copernicus Marine model data, twice a day, for fourteen pilot beaches around the island. The nearest model cells sit &lt;strong&gt;3.1 to 6.9 kilometers offshore&lt;/strong&gt;, and the model speaks from there. What the beach does with that energy is a translation, not a measurement.&lt;/p&gt;

&lt;p&gt;I want that sentence to sit in your chest for a second, because it is the whole piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sea's shape
&lt;/h2&gt;

&lt;p&gt;Within the ARGOS EMODnet window, the deepest cell is &lt;strong&gt;3,841 meters&lt;/strong&gt;. Our bathymetry comes from EMODnet at roughly &lt;strong&gt;100 meters per pixel&lt;/strong&gt;, with contours drawn down to &lt;strong&gt;3,500 meters&lt;/strong&gt;. Kefalonia's west coast is where that deep water leans closest to the shore.&lt;/p&gt;

&lt;p&gt;Along Myrtos' open-water bearing of 291 degrees, the corrected EMODnet contours put 25 m of water about 2.1 km from the shoreline, 50 m at 3.0 km, and 100 m at 4.2 km. The 200 m contour lies about 10.0 km offshore. The beach's assigned CMEMS cell sits 3.36 km from the beach centroid, between the 50 and 100 m contours; at roughly 100 m per pixel, this is shelf-scale context, not swim-zone bathymetry.&lt;/p&gt;

&lt;p&gt;That geometry matters because it is why a beach can look like a swimming pool and behave like something else. The energy does not need a storm to arrive. It needs a direction, a period, and a floor that shoals the right way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The layer
&lt;/h2&gt;

&lt;p&gt;Fourteen beaches. Copernicus Marine Mediterranean forecasts: waves and &lt;strong&gt;two-dimensional surface currents&lt;/strong&gt;, refreshed twice daily. The exposure score is a &lt;strong&gt;direction-gated wave score at 70 percent plus a current score at 30 percent&lt;/strong&gt;. The gate exists because a wave from the wrong direction is noise, and from the right direction it is the whole story.&lt;/p&gt;

&lt;p&gt;The bathymetry you just read is context, not an input. Our exposure score does not use the EMODnet shelf layer directly; the CMEMS models carry their own model-resolution bathymetry. It knows the sea state. We supply the geography.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we could not validate, so we are saying so
&lt;/h2&gt;

&lt;p&gt;There is no independent wave buoy on Kefalonia. The published POSEIDON network has no Kefalonia station; its named Ionian mooring is Pylos. We checked. The answer is no.&lt;/p&gt;

&lt;p&gt;So this layer ships &lt;strong&gt;unvalidated&lt;/strong&gt;, first in the validation queue. The interim path is cross-checking against KefaloniaPress daily beach scores and the quality documentation that ships with the Copernicus products themselves. The first version of this story had a cleaner ending. This is the honest one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;Not a rip-current warning. Not measured beach safety. Not a lifeguard-grade forecast. Not a safe-or-unsafe certification for any beach, ever.&lt;/p&gt;

&lt;p&gt;It is an exposure screening: &lt;em&gt;modelled offshore wave and current conditions, translated into a beach exposure assessment.&lt;/em&gt; Direction-gated, twice daily, bilingual, and public about its own uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ask
&lt;/h2&gt;

&lt;p&gt;If you are a lifeguard, a boat operator, or someone who has watched Myrtos for thirty years: your observations are the validation this layer is missing. The map will only get as honest as the ground truth it is fed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built in Kefalonia. Watching over the places we call home.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;https://argos-geo.org/map&lt;/a&gt; · &lt;a href="https://github.com/argos-geo/kefalonia-digital-twin" rel="noopener noreferrer"&gt;https://github.com/argos-geo/kefalonia-digital-twin&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>geospatial</category>
      <category>marine</category>
      <category>kefalonia</category>
    </item>
    <item>
      <title>The Answer You Didn't Want: Fiskardo Evacuation Routing</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Thu, 17 Sep 2026 15:16:27 +0000</pubDate>
      <link>https://dev.to/ptzivras/the-answer-you-didnt-want-fiskardo-evacuation-routing-4hok</link>
      <guid>https://dev.to/ptzivras/the-answer-you-didnt-want-fiskardo-evacuation-routing-4hok</guid>
      <description>&lt;p&gt;During a wildfire, how long does it take to move people out of Fiskardo by road?&lt;/p&gt;

&lt;p&gt;That was the question. It sounds like a database query, because it is one: load the road network, pick a start, run pgRouting's &lt;code&gt;pgr_dijkstra&lt;/code&gt;, read the cost. Milliseconds of compute. One number.&lt;/p&gt;

&lt;p&gt;The first number the twin gave me was absurd. And my instinct was to distrust the model. That instinct was correct, but for the wrong reasons. The model was not lying. My data was wrong in five ways. The sixth way was me.&lt;/p&gt;

&lt;p&gt;This is the story of all six.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 1: The dead table
&lt;/h2&gt;

&lt;p&gt;The routing query ran cleanly and returned plausible-looking paths. The problem: it was reading an old import table left over from an earlier iteration. The geometry looked like Kefalonia. The connectivity was garbage. The network I was querying was a ghost.&lt;/p&gt;

&lt;p&gt;Lesson one of geospatial debugging: a query that runs is not a query that is right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 2: The real network was somewhere else
&lt;/h2&gt;

&lt;p&gt;The live road network lived in a different table. Not a bug, just archaeology. But it means everything the first queries "proved" was noise. I threw the results away and started over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 3: cost_m is not a distance
&lt;/h2&gt;

&lt;p&gt;The cost column in my routing table was named &lt;code&gt;cost_m&lt;/code&gt;. I assumed meters. It was minutes. (pgRouting itself returns &lt;code&gt;cost&lt;/code&gt; and &lt;code&gt;agg_cost&lt;/code&gt;; the misleading name was ours.)&lt;/p&gt;

&lt;p&gt;Every distance I had reported so far was actually a travel time. This is the kind of error that doesn't crash. It just quietly corrupts every conclusion you draw from it, forever, until someone reads the schema with suspicious eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 4: Mixed SRIDs
&lt;/h2&gt;

&lt;p&gt;Somewhere in the pipeline, two projections had crept in. Geometry in one spatial reference system was being measured against geometry in another. Distances were quietly, consistently wrong by a factor only a geodesist would guess.&lt;/p&gt;

&lt;p&gt;A digital twin with two coordinate systems is not a twin. It is two maps arguing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 5: The row-order bug
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pgr_dijkstra&lt;/code&gt; returns path segments. The results looked scrambled: segments out of order, the route jumping back on itself.&lt;/p&gt;

&lt;p&gt;The bug was mine. I was reading the result rows in the order the database happened to return them instead of ordering by &lt;code&gt;seq&lt;/code&gt;. The path was correct the whole time. I was reading the sentence word by word, in a shuffled order, and calling it gibberish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 6: The road that might not be there
&lt;/h2&gt;

&lt;p&gt;The final answer was now stable. But it was still wrong in a way no query could detect.&lt;/p&gt;

&lt;p&gt;The route took an enormous detour inland, ignoring a 15 km coastal road in favor of 29 km of mountain. The prime suspect is a connectivity gap on the Evreti stretch. A node that doesn't quite snap. Invisible to every attribute query. The autopsy is still open.&lt;/p&gt;

&lt;p&gt;What we know for certain: the router prefers a 29 km detour over a 15 km coastal road, the road's data looks clean, and local knowledge says the road is fine. The model and the map disagree, and the argument is documented.&lt;/p&gt;

&lt;p&gt;A suspected data gap, wearing a software bug's clothes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The answer
&lt;/h2&gt;

&lt;p&gt;After six rounds, the twin gave me its answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agia Effimia Marina. 29.0 km. 44 minutes. 116 road segments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the answer you didn't want. Forty-four minutes is not a comfortable number if you are standing in Fiskardo watching smoke. At every round of this forensic, a prettier wrong answer was available. Faster numbers. Shorter routes. Numbers that would have looked great in a demo.&lt;/p&gt;

&lt;p&gt;The value of this digital twin was never going to be that it produces answers. Any GIS produces answers. The value is that this answer survived six rounds of me trying to break it, and it is still standing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lighthouse test
&lt;/h2&gt;

&lt;p&gt;The same engine, asked a different question, once ranked an abandoned lighthouse on an uninhabited islet as the hardest place to reach in an emergency: 108.1 minutes from the nearest fire station. Island-wide worst. The tower was leveled in the Italian bombings of 1942; what remained was felled by the 1950 earthquake. Technically correct, contextually meaningless.&lt;/p&gt;

&lt;p&gt;Put the two stories together and you get the real design principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The twin finds edge cases and reports reachability honestly. Humans supply context and priority.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A bombed lighthouse and a hospital get the same accessibility score. That is not a flaw in the model. That is the model telling you exactly which decisions it cannot make for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for civil protection
&lt;/h2&gt;

&lt;p&gt;Evacuation planning runs on numbers. If those numbers come from an unvalidated model with a dead table, a minutes-as-meters column, and an unsolved coastal detour, the plan is a rumor with a map attached.&lt;/p&gt;

&lt;p&gt;The uncomfortable number, validated, is worth more than the comfortable one, invented. That is what "no black boxes" means in practice. Not a slogan on an open data page. Six rounds of forensic debugging before a single digit goes public.&lt;/p&gt;

&lt;p&gt;Every model in ARGOS carries its validation with it: what was tested, against which events, what worked, what didn't. This routing layer is no exception.&lt;/p&gt;

&lt;p&gt;The methodology, the code, and every wrong answer along the way are public. A map that argues back must also admit when it is unsure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;https://argos-geo.org/map&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/argos-geo/kefalonia-digital-twin" rel="noopener noreferrer"&gt;https://github.com/argos-geo/kefalonia-digital-twin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/argos-geo/kefalonia-digital-twin/discussions/4" rel="noopener noreferrer"&gt;Previous: Build Log #4, Where the Water Goes&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Built in Kefalonia. Watching over the places we call home.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>gis</category>
      <category>kefalonia</category>
      <category>opendata</category>
    </item>
    <item>
      <title>ARGOS v0.0.1 is live: a digital twin of Kefalonia</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Thu, 20 Aug 2026 16:56:16 +0000</pubDate>
      <link>https://dev.to/ptzivras/argos-v001-is-live-a-digital-twin-of-kefalonia-3i38</link>
      <guid>https://dev.to/ptzivras/argos-v001-is-live-a-digital-twin-of-kefalonia-3i38</guid>
      <description>&lt;p&gt;&lt;em&gt;Three weeks ago I started building a map that&lt;/em&gt; &lt;strong&gt;argues back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Today it is public.&lt;/p&gt;

&lt;p&gt;🗺️ argos-geo.org/map&lt;/p&gt;

&lt;p&gt;What you can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click any building, road, beach, or POI&lt;/li&gt;
&lt;li&gt;See travel time to the fire station and nearest ferry port&lt;/li&gt;
&lt;li&gt;Toggle wildfire risk and flash-flood layers&lt;/li&gt;
&lt;li&gt;Find the lighthouse nobody asked about (102 minutes, boat-only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack: PostGIS + FastAPI + MapLibre GL + PMTiles. Hosted on GitHub Pages. Costs €0/month.&lt;/p&gt;

&lt;p&gt;The data: OpenStreetMap, Copernicus DEM, Sentinel-2, NASA FIRMS, EFFIS. All open. All attributed.&lt;/p&gt;

&lt;p&gt;This is a static snapshot. Phase 2 adds real-time feeds. Phase 3 adds a municipality pilot.&lt;/p&gt;

&lt;p&gt;I wrote about how it happened:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3" class="crayons-story__hidden-navigation-link"&gt;I built a digital twin of a Greek island and it found a lighthouse no one asked about&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ptzivras" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4080444%2F0ea7b5c3-877a-413e-a23c-d34f6c0a8e20.png" alt="ptzivras profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ptzivras" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Panagis Tzivras
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Panagis Tzivras
                
                
              
              &lt;div id="story-author-preview-content-4428099" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ptzivras" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4080444%2F0ea7b5c3-877a-413e-a23c-d34f6c0a8e20.png" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Panagis Tzivras&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3" id="article-link-4428099"&gt;
          I built a digital twin of a Greek island and it found a lighthouse no one asked about
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/geospatial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;geospatial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/postgressql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;postgressql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/digitaltwin"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;digitaltwin&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;Coming next: Build Log #2. Why the model thinks Greek firs burn like pine (and why it is wrong).&lt;/p&gt;




&lt;p&gt;Built in Kefalonia. Watching over the places we call home.&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>digitaltwin</category>
      <category>kefalonia</category>
      <category>maplibre</category>
    </item>
    <item>
      <title># Build Log #1: Processing my first Sentinel-2 pass in 48 hours</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Wed, 19 Aug 2026 16:01:26 +0000</pubDate>
      <link>https://dev.to/ptzivras/-build-log-1-processing-my-first-sentinel-2-pass-in-48-hours-8p9</link>
      <guid>https://dev.to/ptzivras/-build-log-1-processing-my-first-sentinel-2-pass-in-48-hours-8p9</guid>
      <description>&lt;p&gt;&lt;strong&gt;48 hours and moderate sleep later, ARGOS just processed its first Sentinel-2 pass over Kefalonia.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is what that looks like.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I was trying to do
&lt;/h2&gt;

&lt;p&gt;I wanted a baseline vegetation map of the entire island. Not from a government PDF, not from a paid dataset- from the European Space Agency's Sentinel-2 satellite, freely available, updated every 5 days. The goal was simple: download the latest cloud-free acquisition, run it through a processing pipeline, and get an NDVI layer I could query against.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python + &lt;code&gt;rasterio&lt;/code&gt; + &lt;code&gt;xarray&lt;/code&gt; for data handling&lt;/li&gt;
&lt;li&gt;GDAL for reprojection (Sentinel-2 comes in UTM 34N, I need EPSG:4326 for the web map)&lt;/li&gt;
&lt;li&gt;A very patient laptop&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Sentinel-2 data is "free" the same way a 500-piece puzzle is "free"- every piece is there, but no one warned you about step seven. The SAFE format, the multiple granules, the 10m vs 20m resolution bands, the cloud masking- none of it is hard individually, but the pipeline has about twelve steps and step seven fails silently if you forget to resample the 20m bands before stacking.&lt;/p&gt;

&lt;p&gt;I forgot. Twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;A very ugly Python script that I am not proud of but that works. It downloads the granule, filters clouds, calculates NDVI, reprojects to WGS84, and drops a GeoTIFF into a folder that PostGIS will eventually ingest. The script is 180 lines. 40 of them are error handling. That's the honest ratio.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;The NDVI map looks beautiful. Green valleys, dry ridges, the whole island in false colour. But "beautiful" and "useful" are different things. I spent hour 36 staring at a perfectly rendered map and realising I had no idea whether an NDVI of 0.3 meant "stressed vegetation" or "just rocks." The satellite sees what it sees. Context comes from knowing the island.&lt;/p&gt;

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

&lt;p&gt;Layer 2: wildfire risk index. Slope + aspect + NDVI + fuel type. The model will be wrong. It already thinks Greek firs burn like pine. But that's Build Log #2.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/argos-geo/kefalonia-digital-twin/discussions/1" rel="noopener noreferrer"&gt;Full code + methodology on GitHub Discussion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;Live map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argos-geo.org" rel="noopener noreferrer"&gt;ARGOS project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This is Build Log #1 of the ARGOS series. Build Log #2: Why the model thinks Greek firs burn like pine (and why it's wrong).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>satellite</category>
      <category>remotesensing</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>I built a digital twin of a Greek island and it found a lighthouse no one asked about</title>
      <dc:creator>Panagis Tzivras</dc:creator>
      <pubDate>Tue, 18 Aug 2026 18:10:48 +0000</pubDate>
      <link>https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3</link>
      <guid>https://dev.to/ptzivras/i-built-a-digital-twin-of-a-greek-island-and-it-found-a-lighthouse-no-one-asked-about-38n3</guid>
      <description>&lt;p&gt;I built a digital twin of a Greek island and it found a lighthouse no one asked about&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One person, one laptop, zero budget, one island.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Kefalonia is the sixth-largest island in Greece, which sounds impressive until you try to answer a simple question: if a fire starts on that ridge, how long until the fire truck actually arrives?&lt;/p&gt;

&lt;p&gt;I spent my summers on Kefalonia. It's the sixth-largest island in Greece, which sounds impressive until you realise what that means in practice: 254 km of coastline, three municipalities, one fire station with a volunteer watch, and exactly zero real-time flood monitoring. When the August fires hit Zakynthos 20 km away, the civil protection alert arrives by SMS. When the February 2025 storm washed out roads, the information travelled at the speed of someone driving to check.&lt;/p&gt;

&lt;p&gt;I work in data. I know what Copernicus produces. I know what EFFIS tracks. I also know that none of it reaches the municipality inbox in a format anyone can use. The data is free. The gap is distribution.&lt;/p&gt;

&lt;p&gt;So I started building the distribution layer.&lt;/p&gt;




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

&lt;p&gt;ARGOS is an open-source digital twin of Kefalonia. One PostgreSQL/PostGIS database that holds the whole island: 12,217 road segments, 22,013 buildings, 1,831 POIs, 197 beaches, 29 trails, terrain from Copernicus DEM 30 m, vegetation from Sentinel-2, and two hazard layers that have already been tested against reality.&lt;/p&gt;

&lt;p&gt;The stack is deliberately boring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostGIS 16&lt;/strong&gt; for the spatial engine (SQL I can write, no Java servers to maintain)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; for the API (&lt;code&gt;/layers&lt;/code&gt;, &lt;code&gt;/buffer&lt;/code&gt;, &lt;code&gt;/intersect&lt;/code&gt;, &lt;code&gt;/aggregate&lt;/code&gt; - all demo queries &amp;lt; 500 ms)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MapLibre GL + PMTiles&lt;/strong&gt; for the map (9.4 MB single file, served from GitHub Pages, HTTP 206 range requests)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Codespaces&lt;/strong&gt; for the dev environment (&lt;code&gt;docker compose up -d&lt;/code&gt;, zero local setup)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boring stacks ship. This one shipped in six weeks of Saturday mornings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The map that argues back
&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%2Fhd564iemf2raftgdxoij.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%2Fhd564iemf2raftgdxoij.png" alt=" " width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The live map is at &lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;argos-geo.org/map&lt;/a&gt;. Click anything - a building, a beach, a road segment - and it answers. The popup tells you how many minutes that building is from the fire station and the nearest ferry port. Not as-the-crow-flies. As-the-ambulance-drives. Or doesn't drive, as the case may be.&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%2F2xcj7d4wockmj1n3bitv.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%2F2xcj7d4wockmj1n3bitv.png" alt=" " width="339" height="240"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The lighthouse problem
&lt;/h2&gt;

&lt;p&gt;Here's my favourite edge case. The model ran its first accessibility analysis and found the hardest place to reach in an emergency. It wasn't a mountain village. It wasn't a remote beach. It was a single building with a travel time of 102 minutes.&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%2Fokc0smyzb1krrr0po4uz.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%2Fokc0smyzb1krrr0po4uz.png" alt=" " width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's a lighthouse. On an uninhabited islet. Built by the British in 1824, bombed by the Italians in 1942, flattened by the 1953 earthquakes. Accessible only by boat. No roads. No permanent population. The algorithm correctly flagged it as the most isolated structure in Kefalonia.&lt;/p&gt;

&lt;p&gt;The model doesn't know what's "important" to humans. It just knows what's hard to reach. A bombed lighthouse and a hospital get the same accessibility score. Context comes later.&lt;/p&gt;

&lt;p&gt;Oh, and - it's an island.&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%2Fbgqqrvy4ixgw4s8yez1z.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%2Fbgqqrvy4ixgw4s8yez1z.png" alt=" " width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The algorithm knew that too. It just didn't think it was worth mentioning.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hazard layers (or: why I don't trust my own model yet)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ARGOS WATCH: Wildfire Risk v1.1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Five-factor weighted index: slope, fuel proxy (NDVI), road proximity, aspect, and historical burn scars from EFFIS. Validated against 24 real fire perimeters (2017-2026). Burned areas score 63.6 vs the island baseline of 59.1. Very-high-risk enrichment is ~3.7×.&lt;/p&gt;

&lt;p&gt;But here's the honest part: the model thinks Greek fir (&lt;em&gt;Abies cephalonica&lt;/em&gt;) burns like Mediterranean pine. It doesn't. Different fuel load, different crown fire behaviour, different moisture retention. The satellite sees NDVI = 0.6 and says "green = fuel." The local says "fir = doesn't crown-fire like pine." Build Log #2 is about exactly this failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ARGOS WATCH: Flash-Flood / Debris-Flow v1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;D8 drainage network × receiving slope × upstream burn-scar exposure. Validated against the February 2025 storm. The model's top-ranked ravines: Τραπεζάκι 97.5, Πόρος 96.9, Περατάτα 94.6. Those names match the news reports. The model ranked reality correctly.&lt;/p&gt;

&lt;p&gt;Both layers are &lt;strong&gt;screening baselines, not forecasts&lt;/strong&gt;. Directionally validated, not calibrated. I say this explicitly because credibility comes from admitting limitations, not hiding them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture
&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%2Ffy7tz1l8aa47rgb3ocks.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%2Ffy7tz1l8aa47rgb3ocks.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every pipeline step is a numbered, committed script in &lt;code&gt;scripts/&lt;/code&gt;. Every model has an open methodology document with an honest limitations section. The whole stack runs in a GitHub Codespace. The whole thing costs €0/month to host.&lt;/p&gt;

&lt;p&gt;The code is MIT. The derived layers are CC BY 4.0. The sources are all open: OpenStreetMap, Copernicus DEM, Sentinel-2, NASA FIRMS, EFFIS. Attribution is architecture, not decoration.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three sub-brands
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ARGOS GEO&lt;/strong&gt; - the twin itself: data, pipelines, API, map&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARGOS WATCH&lt;/strong&gt; - hazard intelligence: wildfire + flash-flood screening, validated in the open&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARGOS COMMONS&lt;/strong&gt; - whatever this grows into next, with the people who gather around it&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;v0.0.1 is a static twin: snapshots, not streams. Phase 2 adds real-time feeds (Open-Meteo weather, ADS-B aircraft, EFFIS/Copernicus EMS alerts), a 7-day hazard outlook, and historical playback. Phase 3 is a municipality pilot: three months, no cost, they provide feedback.&lt;/p&gt;

&lt;p&gt;The long-term goal isn't to replace civil protection. It's to give them a layer they don't have: local granularity on top of satellite data. EFFIS shows Greece burns ~50,000 hectares annually. ARGOS is trying to answer which 16 km² of Kefalonia burn &lt;em&gt;first&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🗺️ &lt;strong&gt;Live map:&lt;/strong&gt; &lt;a href="https://argos-geo.org/map" rel="noopener noreferrer"&gt;argos-geo.org/map&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/argos-geo/kefalonia-digital-twin" rel="noopener noreferrer"&gt;github.com/argos-geo/kefalonia-digital-twin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://argos-geo.org" rel="noopener noreferrer"&gt;argos-geo.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📧 &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:hello@argos-geo.org"&gt;hello@argos-geo.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🦋 &lt;strong&gt;Bluesky:&lt;/strong&gt; &lt;a href="https://bsky.app/profile/argos-geo.org" rel="noopener noreferrer"&gt;@argos-geo.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐘 &lt;strong&gt;Mastodon:&lt;/strong&gt; &lt;a href="https://mapstodon.space/@argos_geo" rel="noopener noreferrer"&gt;@argos_geo@mapstodon.space&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐦 &lt;strong&gt;X:&lt;/strong&gt; &lt;a href="https://x.com/argos_geo" rel="noopener noreferrer"&gt;@argos_geo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built in Kefalonia. Watching over the places we call home.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you know Mediterranean fuel models, Greek forestry data, or municipal civil protection workflows, I need you. Drop a comment or email &lt;a href="mailto:hello@argos-geo.org"&gt;hello@argos-geo.org&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Build Log #0 - the debut. Build Log #1 (Sentinel-2 processing and the crime scene Python script) is already live and a few days old, which in internet years makes it practically vintage. Build Logs #2-10 are coming: the Greek fir problem, drainage mapping, the Vardiani lighthouse, stack decisions, containerisation, the Copernicus → inbox gap, API-first architecture, hosting on Cloudflare Pages, and what "building in public" actually means when your model is wrong.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>geospatial</category>
      <category>postgressql</category>
      <category>digitaltwin</category>
    </item>
  </channel>
</rss>
