Store and access data published by OwnTracks apps
The OwnTracks Recorder is a lightweight program for storing and accessing location data published via MQTT (or HTTP) by the OwnTracks apps. It is a compiled program which is easy to install and operate even on low-end hardware, and it doesn't require an external database.
There are two main components: the Recorder obtains data via MQTT subscribes or HTTP POST, stores the data in plain files and serve it via its built-in REST API, and the ocat command-line utility reads stored data in a variety of formats.
We developed the Recorder as a one-stop solution to storing location data published by our OwnTracks apps (iOS and Android) and retrieving this data. Our previous offerings (m2s, o2s/Pista) also work of course, but we believe the Recorder is best suited to most environments.
See also HOOKS.md, DESIGN.md, and STORE.md for more information on Lua hooks, program design, and storage layout, respectively.
recorderot-recorder optionsocat
recorderThe Recorder serves two purposes:
We provide ready-to-run packages for a limited number of platforms on our package repository, and we provide a Docker image of the Recorder directly from the Docker hub.
If those don't work for you, you can build from source.
We create packages for releases for a few distributions. Please note:
Binaries (ocat, ot-recorder) from these packages run setuid to user owntracks so that they work for all users of the system. Note that, say, certificate files you provide must therefore also be readable by the user owntracks.
Debian 10 "Buster":
Debian 11 "Bullseye":
Debian 12 "Bookworm":
Debian 13 "Trixie":
…--geokey "opencage:xxxxxxxxxxxxxxxxxxxxxx" # for OpenCage --geokey "xxxxxxxxxxxxxxxxxxxxxx" # for Google --geokey "revgeod:localhost:8865" # for Revgeod (host:port)
…OTR_STORAGEDIR="/var/spool/owntracks/recorder/store"
…server { listen 8080; server_name 192.168.1.130;
location / {
root html;
index index.html index.htm;
}
# Proxy and upgrade WebSocket connection
location /owntracks/ws {
rewrite ^/owntracks/(.*) /$1 break;
proxy_pass http://127.0.0.1:8083;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /owntracks/ {
proxy_pass http://127.0.0.1:8083/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
# OwnTracks Recorder Views (requires /view, /static, /utils)
location /owntracks/view/ {
proxy_buffering off; # Chrome
proxy_pass http://127.0.0.1:8083/view/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
location /owntracks/static/ {
proxy_pass http://127.0.0.1:8083/static/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
location /owntracks/utils/ {
proxy_pass http://127.0.0.1:8083/utils/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
# HTTP Mode
location /owntracks/pub {
auth_basic "OwnTracks pub";
auth_basic_user_file /usr/local/etc/nginx/owntracks.htpasswd;
proxy_pass http://127.0.0.1:8083/pub;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
# Optionally force Recorder to use username from Basic
# authentication user. Whether or not client sets
# X-Limit-U and/or uses ?u= parameter, the user will
# be set to $remote_user.
proxy_set_header X-Limit-U $remote_user;
}
}
### Apache
This will hand URIs which begin with `/owntracks/` to the Recorder.
ProxyPass /owntracks/ws ws://127.0.0.1:8083/ws keepalive=on retry=60 ProxyPassReverse /owntracks/ws ws://127.0.0.1:8083/ws keepalive=on
ProxyPass /owntracks http://127.0.0.1:8083/ ProxyPassReverse /owntracks http://127.0.0.1:8083/
### [Caddy](https://caddyserver.com)recorder.example.org {
encode gzip
reverse_proxy http://localhost:8083
basicauth * {
# create new password hashes with caddy hash-password --plaintext yourpasswordhere
john_doe JDJhJDE0JDBNc0FyNGd3b3JzejBTaExUZnkxdnV2THZSUUdHNzJYZmdVTzN3NlZXcjh2YXJLQi5KY04u
}
}
…$ curl http://127.0.0.2:8083/api/0/last -d user=demo -d device=iphone [ { "tst": 1440405601, "acc": 10, "_type": "location", "alt": 262, "lon": 13.60279820860699, "vac": 6, "vel": 18, "lat": 51.06263391678321, "cog": 82, "tid": "NE", "batt": 99, "username": "demo", "device": "iphone", "topic": "owntracks/demo/iphone", "ghash": "u31dmx9", "isotst": "2015-08-24T08:40:01Z", "disptst": "2015-08-24 08:40:01", "cc": "DE", "addr": "E40, 01156 Dresden, Germany" } ]
#### Display map with points starting at a particular date
By specifying a `format` we can produce GeoJSON, say. Normally, the API retrieves the last 6 hours of data but we can extend or limit this with the `from` and `to` parameters.
http://127.0.0.2:8083/map/index.html?user=demo&device=iphone&format=geojson&from=2014-01-01
…xml
1440405601 10.000000 262 13.602798 6.000000 18 51.062634 82 NE 99 demo iphone owntracks/demo/iphone u31dmx9 2015-08-24T08:40:01Z 2015-08-24 08:40:01 DE E40, 01156 Dresden, Germany
…%Y-%m-%dT%H:%M:%S %Y-%m-%dT%H:%M %Y-%m-%dT%H %Y-%m-%d %Y-%m
…$ ocat --list { "results": [ "demo" ] }
From which devices has user `demo` published data?
$ ocat --list --user demo { "results": [ "iphone" ] }
#### Show the last position reported by a user
Where was `demo`'s iPhone last seen? (Omit `--user` and `--device` to get LAST for all users and devices.)
$ ocat --last --user demo --device iphone [ { "tst": 1440405601, "acc": 10, "_type": "location", "alt": 262, "lon": 13.60279820860699, "vac": 6, "vel": 18, "lat": 51.06263391678321, "cog": 82, "tid": "NE", "batt": 99, "username": "demo", "device": "iphone", "topic": "owntracks/demo/iphone", "ghash": "u31dmx9", "isotst": "2015-08-24T08:40:01Z", "disptst": "2015-08-24 08:40:01", "cc": "DE", "addr": "E40, 01156 Dresden, Germany" } ]
…$ ocat --user demo --device iphone --limit 4 --format csv --fields isotst,vel,addr isotst,vel,addr 2015-08-24T08:40:01Z,18,"E40, 01156 Dresden, Germany" 2015-08-24T08:35:01Z,40,"E40, 01723 Wilsdruff, Germany" 2015-08-24T08:30:00Z,50,"A14, 01683 Nossen, Germany" 2015-08-24T08:24:59Z,40,"A14, 04741 Roßwein, Germany"
## Reverse Geo
If not disabled with option `--norevgeo`, the Recorder will attempt to perform a reverse-geo lookup on the location coordinates it obtains. These can be either
1. obtained via a Lua function you define (see [doc/HOOKS.md](doc/HOOKS.md))
2. obtained via a call to one of the supported reverse geocoders (see `--geokey`)
Results of lookups are stored in an LMDB database. If a lookup is not possible, for example because you're over quota, the service isn't available, etc., Recorder keeps tracks of the coordinates which could *not* be resolved in a file named `missing`:
$ cat store/ghash/missing u0tfsr3 48.292223 8.274535 u0m97hc 46.6527
No open issues yet, or sync has not completed.