Location Database — Usage

See also: location-db • schema • architecture

MCP tool: pg_query

pg_query accepts sql plus optional login (owntracks_ro default, owntracks_rw, owntracks_adm), with permissions enforced by PostgreSQL. SELECT/WITH/RETURNING return row arrays; commands without result columns return status. Use explicit LIMIT; no 500-row cap. Restoration deployed on gravlax on 2026-10-12; current Misc version is 2.0.0. Authenticated live MCP verified the login schema, all identities, default read-only enforcement, rw CRUD, adm CREATE/DROP, RETURNING and empty results. The disposable table was removed.

The server does not retry SQL automatically. The requesting LLM decides whether to retry after inspecting the error. "Please try again" is generic error text, not proof of a stale connection. For a write with an uncertain outcome, check the resulting state before repeating it.

PostGIS quick reference

Always cast geom to ::geography for metre-accurate distance. Use ST_AsGeoJSON(geom) to return geometry as readable GeoJSON.

Example queries

Nearest approach to a point (e.g. Bristol Temple Meads, lat=51.449, lon=-2.581):

SELECT to_timestamp(tst) AT TIME ZONE 'Europe/London' AS t,
       lat, lon,
       round(ST_Distance(
           geom::geography,
           ST_SetSRID(ST_MakePoint(-2.581, 51.449), 4326)::geography
       )::numeric, 0) AS dist_m,
       conn, ssid
FROM locations
ORDER BY geom <-> ST_SetSRID(ST_MakePoint(-2.581, 51.449), 4326)
LIMIT 10;

All fixes within 500m of a point:

SELECT to_timestamp(tst) AT TIME ZONE 'Europe/London' AS t,
       lat, lon, acc, conn, ssid
FROM locations
WHERE ST_DWithin(
    geom::geography,
    ST_SetSRID(ST_MakePoint(-2.581, 51.449), 4326)::geography,
    500
)
ORDER BY tst;

Speed between two timestamps (haversine via PostGIS):

WITH ordered AS (
  SELECT tst, geom,
         lag(tst)  OVER (ORDER BY tst) AS prev_tst,
         lag(geom) OVER (ORDER BY tst) AS prev_geom
  FROM locations
  WHERE tst BETWEEN 1783900000 AND 1783950000
)
SELECT to_timestamp(tst) AT TIME ZONE 'Europe/London' AS t,
       round((ST_Distance(geom::geography, prev_geom::geography)
              / (tst - prev_tst) * 2.23694)::numeric, 1) AS speed_mph
FROM ordered
WHERE prev_tst IS NOT NULL AND (tst - prev_tst) BETWEEN 1 AND 300
ORDER BY tst;

WiFi-only fixes in a time window:

SELECT to_timestamp(tst) AT TIME ZONE 'Europe/London' AS t,
       ssid, ST_AsGeoJSON(geom) AS geojson, batt
FROM locations
WHERE conn = 'w'
  AND tst > extract(epoch FROM now() - interval '24 hours')
ORDER BY tst;

Today's fixes with geometry as GeoJSON:

SELECT to_timestamp(tst) AT TIME ZONE 'Europe/London' AS t,
       ST_AsGeoJSON(geom)::json AS geojson,
       acc, conn, ssid, batt
FROM locations
WHERE tst >= extract(epoch FROM current_date AT TIME ZONE 'Europe/London')
ORDER BY tst;

When asked a location-dependent question ("where am I", "nearest station", "how far to X"), always re-query SELECT ... ORDER BY tst DESC LIMIT 1 fresh — even if a position was already fetched earlier in the same conversation. The phone keeps reporting fixes in the background, so an earlier answer goes stale within minutes, especially in a moving vehicle. Caught in practice on 15 July 2026: answered "nearest station" using a fix from ~30 min prior instead of re-checking, giving a wrong answer (Radyr instead of Pencoed after the car had moved ~15 miles further west).

Agent best practice: don't reuse a cached fix

SSID interpretation — identify the router before inferring movement

The meaning of conn='w' depends on WHICH SSID. c15 is John's fixed-line home router (normally plugged into Virgin Media or a fibre ISP such as TrueSpeed), so a fix on c15 does indicate a settled home/stationary location. c13 is a mobile router with a SIM in it (a portable cellular hotspot) — it travels with him, so a fix on c13 carries NO location implication and may be moving (e.g. in a vehicle). Exception: during a provider outage he plugs c15 into c13 for failover, so c13 then backhauls the fixed line and could also be at home. Rule: check the SSID first — c15 ⇒ likely home/stationary; c13 ⇒ position only, could be in motion. Never treat WiFi generically as a proxy for 'home', and always re-query the latest fix regardless.

Database logins for MCP access (2026-09-18)

Three logins now exist on the owntracks database on gravlax, so that write access can be granted to an MCP client by choosing an identity rather than by relaxing a filter in code. John's design (2026-09-18): "the mcp connection gets a new parameter which is the name of the database login. the default would be old behaviour… Now the RE will have to go; modifying it will get fiddly and error prone." The principle is that the database decides what each login may do, and the connector simply picks which one to connect as.

LoginSELECTINSERT / UPDATE / DELETECREATE new objectsALTER existing tables
owntracks_ro (default)yesnonono
owntracks_rwyesyesnono
owntracks_admyesyesyestravel-owned tables only

Socket access restricted 2026-09-18. The PostgreSQL unix socket is now srwxrwx--- postgres:pgsocket (was srwxrwxrwx), set via unix_socket_group = 'pgsocket' and unix_socket_permissions = 0770. Only the OS users john and postgres can reach the database at all; www-data now gets "Permission denied" on connect. If a new service ever needs database access, add its OS user to the pgsocket group — otherwise it will fail to connect in a way that looks like a configuration error rather than a permission one. These are postmaster-context settings: changing them needs a restart, not a reload.

{"sql":"SELECT current_user AS login"}
{"sql":"SELECT current_user AS login","login":"owntracks_rw"}
{"sql":"SELECT current_user AS login","login":"owntracks_adm"}

Beta-test fixes deployed 2026-09-18. The existing owntracks_we role was renamed to owntracks_rw, preserving its grants; socket authentication and the MCP login enum now use owntracks_rw. For objects created as owntracks_adm in public, default privileges now grant SELECT on tables to owntracks_ro; SELECT/INSERT/UPDATE/DELETE on tables and USAGE/SELECT on sequences to owntracks_rw. These defaults apply to future objects created by that role, not objects created after SET ROLE to another owner. Equivalent grants were applied to the existing admin-owned mcptest table and sequence. The MCP description contains only a short purpose and pointers to this note and location-db/schema. Public HTTPS MCP discovery and real writes to a newly admin-created table passed, including default read-only access and denied read-only writes; the generated test table was removed. ChatGPT may retain an old schema until the Misc connector is refreshed/reconnected; that client-side refresh remains to be verified. Backups on gravlax: /home/john/tmp/owntracks-schema-before-rw-20260918.sql, /etc/postgresql/15/main/pg_hba.conf.bak-codex-rw-20260918, and /home/john/py/gdata-server/.misc_mcp_server.py.backup-codex-rw-20260918.

Deployment, verification and source synchronization record (2026-10-12): Misc reconciliation and deployment record. Current version 2.0.0; GitHub origin/m commit c0b4fec. Gravlax service restarted and verified. Offline tests: 154 passed, 16 version-specific skips.

Restoration completed 2026-10-12. Source fix commit 9732bc7; merged with newer origin/m and pushed as e1fca6e. Existing Outlook plugins preserved. Added missing misc_webdav.py on gravlax. Rollback source: /home/john/py/gdata-server/.misc_mcp_server.py.backup-restore-20261012. Offline verification: 154 passed, 16 version-specific skips. The September deployment records above are historical; the October SELECT-only regression was observed and has now been repaired. A ChatGPT session may continue exposing its previously loaded sql-only schema until the Misc connector is refreshed or reconnected.

Current Misc server version is 2.0.0, explicitly requested and deployed 2026-10-12. GitHub origin/m commit c0b4fec. Authenticated MCP initialization advertises 2.0.0; login schema and CRUD permissions reverified successfully after restart.

version 8  ·  updated 2026-10-12