Skip to content

bermana-net/daco

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

daco — a self-hosted GPS day counter

Know exactly how many days you have spent in each country, on your own server, with your own data.

Built by Artem Berman · www.bermana.net


daco turns the GPS track from your phone into a clean dashboard of days-per-country and checks them against residency and visa limits you define (Cyprus 60/183, Schengen 90/180, and any country you add). It runs entirely on a server you control. Your location data never leaves your machine. Optionally, it can timestamp a tamper-evident log of your track into the Bitcoin blockchain for free.

No accounts, no cloud service, no tracking. Just your phone, your server, and a single HTML report you open whenever you want.

What it does

  • Counts days spent in each country from your own GPS log.
  • Checks them against limits you set: calendar-year caps (e.g. Cyprus 183) and rolling windows (e.g. Schengen 90/180).
  • Produces one self-contained HTML report: a days-by-country donut, a per-month bar chart, a presence calendar, and a "days used / days left" table per rule.
  • Interface switches between English, Russian and Spanish; country names translate automatically for any country (CLDR).
  • Flags physically impossible "teleport" points (bad fixes / spoofing) and attributes each day to the country you actually spent it in.
  • Optionally anchors a hash-chain of your track into Bitcoin via OpenTimestamps, so the record cannot be silently rewritten later.

How it works

 phone (GPSLogger)  --SFTP-->  server folder (*.gpx)
                                      |
                            daily cron (run.sh)
                                      |
                      count.py  ->  report.py  ->  out/report.html
                                      |
                 (optional) anchor.sh -> Bitcoin timestamp (OpenTimestamps)

Your phone logs GPS to a daily .gpx file and auto-uploads it to a folder on your server over SFTP. A nightly cron job reads every .gpx, works out the country for each day, and rebuilds the report. You open the report through your SFTP app or any browser.


What you need

  • A server: any Linux box (tested on Ubuntu 24.04), with Python 3, cron, gzip, and an SSH/SFTP server (openssh-server) so the phone can upload to it. A small cloud VM is plenty.
  • A phone: Android, with the free GPSLogger app.

1. Server setup

Clone the project and install the dependencies:

git clone https://github.com/bermana-net/daco.git
cd daco
pip3 install reverse_geocoder babel --break-system-packages
# optional, only if you want the Bitcoin anchor:
pip3 install opentimestamps-client --break-system-packages

Create the folder the phone will upload into (any path you like):

mkdir -p ~/gpslogger

By default daco reads GPS files from /home/ubuntu/gpslogger. If your path differs, point it there with an environment variable:

export DACO_GPS_DIR="$HOME/gpslogger"

(Add that line to the cron command too, or edit the default at the top of count.py and in anchor.sh.)

2. Set up GPSLogger on Android

GPSLogger is free and open source. It is not on Google Play; get it from one of:

Then configure it:

Logging (menu → Logging details / Performance)

  • Log to GPX. GPSLogger writes one file per day named YYYYMMDD.gpx.
  • Set a logging interval that gives at least a few points per day (e.g. every 15–60 minutes, or on movement). For day-counting you only need presence, not a dense track.
  • Allow both GPS and network providers.

Keep it running (Android system settings)

  • Enable Start on bootup in GPSLogger.
  • Turn off battery optimization for GPSLogger (Android → Apps → GPSLogger → Battery → Unrestricted), or Android may kill it.

Auto upload over SFTP (menu → Auto send, email and upload)

  • Turn on Allow auto sending.
  • Set How often (minutes) to taste; every 60 minutes is fine. (It sends a file by clock interval, not at a fixed time of day.)
  • Enable the SFTP target and fill in:
    • Server: your server's address (IP or hostname)
    • Port: 22
    • User: your server username
    • Auth: password or, better, an SSH private key
    • Remote directory: the upload folder you created, e.g. /home/<you>/gpslogger

From now on the phone drops a daily .gpx into that folder.

3. Run it

cd ~/daco
bash run.sh

This reads the GPS files, recomputes the counts, and writes out/report.html. Open that file in your SFTP app (tap it; it renders in the browser) or in any browser. It is fully self-contained, no internet needed to view.

To refresh it automatically every night, add a cron line:

(crontab -l 2>/dev/null; echo "0 3 * * * /bin/bash $HOME/daco/run.sh >> $HOME/daco/daco.log 2>&1") | crontab -

4. Configure your limits

Edit rules.json. Each rule has a scope (a country or the Schengen zone), a day limit, a basis, and a description in three languages (the country name is filled in and translated automatically):

{
  "rules": [
    {"id": "cy_183", "scope": {"country": "CY"}, "limit": 183, "basis": "calendar_year",
     "desc": {"en": "183-day tax residency", "ru": "183 дня, налоговое резидентство", "es": "residencia fiscal 183 días"}},
    {"id": "schengen_90", "scope": {"zone": "schengen"}, "limit": 90, "basis": "rolling_180",
     "desc": {"en": "90/180", "ru": "90/180", "es": "90/180"}}
  ]
}
  • scope: {"country": "XX"} (ISO-3166 alpha-2) or {"zone": "schengen"}.
  • basis: calendar_year (current year) or rolling_180 (last 180 days including today).

5. The report

Open out/report.html. Top-right buttons switch the interface between EN / RU / ES. You get the limit table (used / left, with a colour that turns amber near the cap and red when over), a days-by-country donut, a per-month bar chart, a presence calendar, and a list of flagged days.

6. Optional: timestamp into Bitcoin

anchor.sh keeps an append-only hash-chain of your points (chain.jsonl), takes a daily gzip snapshot, and timestamps it into Bitcoin with OpenTimestamps. It is free, needs no wallet, and is fully separate from the counter: if it ever fails, the report is unaffected.

pip3 install opentimestamps-client --break-system-packages
bash anchor.sh                       # run once, check the anchors/ folder
# daily, after the counter:
(crontab -l 2>/dev/null; echo "30 3 * * * /bin/bash $HOME/daco/anchor.sh >> $HOME/daco/anchor.log 2>&1") | crontab -

Verify any day later (after the Bitcoin block confirms, a few hours):

ots upgrade anchors/*.ots
ots verify anchors/chain_YYYYMMDD.jsonl.gz.ots

What this proves and does not prove. A Bitcoin timestamp freezes the data from the moment it is anchored, so anchored history cannot be rewritten afterwards. It does not authenticate the GPS readings themselves, and it does not turn a reconstructed backfill into a real-time record. Treat the log as a contemporaneous journal and cross-check it against independent records (card transactions, carrier roaming, flights) for anything that matters.

How the day logic works

Each GPS point is reverse-geocoded to a country offline. Points are grouped by day; the day is attributed to the dominant country (where most of that day's points are). Isolated points that imply impossible travel speed (a "teleport", typically a bad network fix or a spoof) are excluded from the count, and the day is flagged so you can review it.

Privacy

Your location data stays on your server. The included .gitignore keeps it out of git: data/, out/, anchors/, chain.jsonl, every .gpx, .ots, .gz and log. Only the code is ever published. Never commit those files.

License

MIT. Copyright (c) 2026 Artem Berman. See LICENSE.

Made by Artem Berman — www.bermana.net

About

Days Counter (DACO) — self-hosted GPS day counter for residency and visa limits. Private, free, runs on your own server.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors