Geogram is designed with privacy as a default. There are no accounts, no cloud storage, and no telemetry. This page documents exactly what happens with your data across every communication layer.
Geogram's decentralized architecture means your data is primarily under your control. Here is how data rights and deletion work within the system.
By design, Geogram stores all user data locally on the user's own device. There are no Geogram-operated cloud servers, no user accounts, and no centralized databases. This means:
The only personal identifier is a cryptographic keypair (npub/nsec) generated locally on the device.
Users have full control over local data deletion:
~/.local/share/geogram/ removes all data from the device.Since all data is file-based and stored under a single directory, complete deletion is straightforward and verifiable.
When you publish content to a station (alerts, places, events, emails, blog posts), copies of that data reside on the station server. Since stations are community-operated, data deletion depends on the station operator:
All Geogram data is stored in open, human-readable formats (NOSTR event JSON, markdown files, plain text). The export feature allows you to export all profiles to a file at any time.
Since data lives on your filesystem under ~/.local/share/geogram/, you can always access, copy, or move your data using standard file management tools. There is no vendor lock-in and no proprietary database format.
Geogram identifies users by cryptographic keys (npub/nsec), not by real names, email addresses, or phone numbers. A callsign (e.g. "X3ABCD") serves as a human-readable identifier.
Users can generate a new identity at any time from the Profile settings, which produces a fresh keypair and callsign. There is no link between old and new identities unless the user explicitly establishes one.
If you operate a public station that stores data from other users, you may be considered a data controller under GDPR. Station operators should consider:
For personal/family stations, GDPR's household exemption typically applies.
Geogram continuously evaluates all available transports to find the most optimal path to reach each device. The connection manager acts as a transport-agnostic messaging hub that selects the best route based on configurable routing strategies.
When sending a message, the ConnectionManager queries the active routing strategy to rank available transports. It performs parallel reachability checks (with a 2-second timeout) across all transports and sends through the first one that can reach the target device.
If the selected transport fails, the system automatically falls back to the next available one. If all transports fail and the message is queued, a background processor retries every 30 seconds. The message queue holds up to 1000 pending messages.
lib/connection/connection_manager.dartFour routing strategies are available, with PriorityRoutingStrategy as the default:
Each transport has a fixed priority value. Lower numbers are preferred:
Message ID tracking ensures no duplicates are delivered when a message is attempted across multiple transports.
When internet connectivity is available, Geogram can connect to station servers to relay messages, synchronize data, and enable peer-to-peer connections between devices that aren't on the same local network.
Stations are community-owned servers. Any device can be a station: a phone, a laptop, a Raspberry Pi, or an ESP32 microcontroller. Geogram does not operate centralized servers.
Connections use WebSocket on the default port 3456, though stations may also listen on ports 8080, 80, 8081, 3000, or 5000.
Each station runs a local NOSTR relay (NIP-01 compatible) and optionally a Blossom file storage service for media attachments.
lib/services/station_server_service.dartOnce connected to a station, the server sends a PING every 30-45 seconds and expects a PONG response to maintain the connection.
On disconnection, the client automatically reconnects using exponential backoff. A 5-second grace period allows brief network interruptions without triggering reconnection.
There is no background polling, no heartbeat to external servers, and no "phone home" behavior. Connections only happen to stations you explicitly connect to.
lib/services/websocket_service.dartStation relays support NIP-42 authentication for write operations. When a relay is restricted (associated with a specific callsign path like wss://station/x1abcd), only the profile owner and their followed contacts can write events.
Open relays (root path) accept events from any authenticated author. Authentication uses a cryptographic challenge-response based on NOSTR signatures.
lib/services/nostr_relay_service.dartStations can serve as file storage nodes using the Blossom protocol. Files are uploaded as raw bytes or multipart, stored locally on the station, and identified by content hash.
Default limits: 1024 MB total storage, 10 MB per file. Blobs from followed authors are automatically replicated from NOSTR event tags.
Files never leave the station unless explicitly requested by a connected device. There is no replication to external cloud services.
lib/services/nostr_blossom_service.dartOn a local network, Geogram discovers other devices by scanning for stations on known ports. LAN has transport priority 10 due to its low latency and direct connectivity.
Discovery uses a phased HTTP scan approach. Primary ports 3456 and 8080 are scanned first. If needed, secondary ports 80, 8081, 3000, and 5000 are tried.
Scans run every 5 minutes after an initial 5-second startup delay. Each request has a 400ms timeout. Maximum 50 concurrent connection attempts to avoid flooding the network.
lib/services/station_discovery_service.dartWhen a device responds to a discovery scan at /api/status, it returns:
No authentication is required for discovery reads.
lib/connection/transports/lan_transport.dartBLE enables device discovery and communication within a 10-100 meter range without any network infrastructure. It serves as the last-resort transport (priority 40) when no WiFi or internet is available.
Geogram devices advertise using BLE service UUID 0000ffe0-... with a marker byte 0x3E ('>') in the advertising data to identify themselves as Geogram nodes.
When two devices discover each other, they perform a HELLO handshake through GATT characteristics:
0000fff1-...0000fff2-...The HELLO event is a NOSTR-signed message containing identity information. The following data is transmitted during the handshake:
The HELLO event is cryptographically signed, preventing identity spoofing.
lib/services/ble_message_service.dartBLE message service supports data transfer, broadcast messaging, chat, and deflate compression. Messages larger than the BLE MTU are chunked automatically.
Each device is identified by a callsign-deviceId pair (e.g. "X3ABCD-7"), compatible with APRS SSID format from amateur radio. The device ID ranges from 1-15.
This stable identification scheme works even when BLE MAC addresses change (which happens regularly on most platforms for privacy). Identity is always verified through NOSTR signature on the HELLO event.
Geogram devices can connect directly via USB-C cable using the Android Open Accessory (AOA) protocol. This is the highest-priority transport (priority 5) — the fastest, most reliable, and fully offline connection method.
When two devices are connected via USB-C, one acts as the "host" (initiates the AOA handshake) and the other as the "accessory". The host sends control transfers to identify itself as a Geogram device (manufacturer: "Geogram", model: "Geogram Device"), then the accessory re-enumerates with Google's USB accessory VID/PID.
Once connected, both devices open bulk endpoints for bidirectional data transfer. Messages use a 4-byte length-prefix framing with JSON UTF-8 payloads, supporting API requests, direct messages, chat messages, and system pings.
lib/connection/transports/usb_aoa_transport.dartUSB AOA provides practical throughput of 30-40 MB/s over USB 2.0 High Speed with latency under 5ms. The wired connection has zero packet loss and no radio interference, making it ideal for large file transfers.
Auto-reconnect with exponential backoff (1s, 2s, 4s) handles cable disconnections gracefully. A callsign exchange during the initial handshake establishes device identity.
lib/services/usb_aoa_service.dartOn Linux, the implementation uses USBDEVFS_CONTROL and USBDEVFS_BULK ioctls with poll-based async reads. On Android, broadcast receivers handle USB attachment, detachment, and permission events.
USB connections are inherently private: data travels through a physical cable and cannot be intercepted over the air. No network stack is involved, which eliminates an entire class of remote attack vectors.
All messages transmitted over USB still carry NOSTR signatures for identity verification and integrity, maintaining the same trust guarantees as wireless transports.
docs/connections/geogram-usb-aoa-briefing.mdWebRTC enables direct peer-to-peer connections between devices over the internet, without routing data through a relay server. Both devices must be connected to the same station for signaling.
WebRTC is used for direct device-to-device data transfer over the internet. It has medium-high transport priority (15), preferred over station relay but below LAN and USB.
The data channel is labeled geogram with protocol geogram-p2p, configured for ordered, reliable delivery. All data in transit is encrypted by WebRTC's built-in DTLS encryption.
To establish a WebRTC connection, both devices must be connected to the same station. The station acts as a signaling relay, forwarding:
webrtc_offer - SDP offer (connection proposal)webrtc_answer - SDP answer (acceptance)webrtc_ice - ICE candidates (network paths)webrtc_bye - Connection teardownSignaling messages are wrapped in NOSTR events. Once the P2P connection is established, data flows directly between devices without passing through the station.
Geogram takes a privacy-first approach to STUN. Instead of using third-party STUN servers (Google, Twilio, Mozilla), each station runs its own STUN server on UDP port 3478.
The STUN server implements RFC 5389 Binding requests and responds with XOR-MAPPED-ADDRESS. It does not log client IP addresses. XOR encoding of addresses prevents packet spoofing attacks.
By default, the STUN server list is empty. Your IP address is never leaked to third-party infrastructure for NAT traversal.
lib/services/stun_server_service.dart · lib/services/webrtc_config.dartWebRTC connections are established with strict timeouts to prevent hanging:
Trickle ICE is used (candidates sent as gathered) for faster connection establishment.
When you connect to a station and publish content, some data is stored on the station to make it accessible to other users. Each user can designate a preferred station for their primary data sync.
Each user designates a preferred station for data sync. This is the primary station to which alerts, places, events, emails, and other content are published. The default preferred station is wss://p2p.radio.
Station preference is stored in your profile configuration. Apps call StationService().getPreferredStation() before any sync operation to determine where data should go.
Stations act as email relays with an SMTP server and client. Emails for online recipients are delivered immediately via WebSocket. For offline recipients, emails are encrypted with the recipient's npub (ECIES encryption) and cached in an email-cache/ directory on the station.
Cached emails are limited to 10 MB per recipient and auto-expire after 24 hours. When external emails are sent (to non-station addresses), they enter a three-tier approval queue: blocklist, pending review, or allowlist. DKIM signing is supported for domain reputation.
Email storage uses thread-based organization with inbox/, outbox/, sent/, spam/, garbage/, drafts/, archive/, and labels/ directories, each organized by year.
When you create an alert or place, it is signed as a NOSTR event (kind 30078) and published to your preferred station. The station stores the event in its relay database and makes it queryable by other connected users.
Places are uploaded with their description and images via HTTP to the station. Alerts include share summaries with confirmed/failed/skipped counts so you know exactly what was delivered.
lib/services/alert_sharing_service.dart · lib/services/place_sharing_service.dartThe general pattern for all user-contributed data is:
You always retain your local copy. Station storage is a relay — it distributes your content but does not hold the only copy.
All data in Geogram is stored locally on your device. There is no cloud sync, no remote backup service, and no data leaving your device unless you explicitly send it to another device or station.
All data lives under ~/.local/share/geogram/:
profiles/{callsign}/ - Per-profile data (events, places, blogs, contacts, settings)chat/ - Chat messages, shared across profilesstation/ - Station relay data when running as a serverEach app stores its data in human-readable formats (mostly NOSTR event JSON) within the profile directory.
lib/services/storage_config.dartSensitive data can be stored in an encrypted archive using AES-256-GCM encryption. The encryption key is derived from your NOSTR private key (nsec) using HKDF key derivation.
The archive is a SQLite database with a files table for metadata and a chunks table for encrypted file content. Data is flushed every 30 seconds and archive connections are reused for performance.
Chat messages are cached in memory with a 5-second TTL to avoid repeated file I/O. Each conversation (identified by callsign) has a separate cache.
Direct messages are stored as files in the chat/ directory in NOSTR event format. Unsent messages are queued in a DM queue with exponential backoff retry, and stored locally until delivery is confirmed (up to 30 days).
Map tiles are cached locally for offline access. Stations can cache regional tiles so nearby devices can fetch them without internet access.
Media files (images, voice messages, attachments) are stored in the profile directory alongside the events that reference them. Station Blossom storage is separate and subject to its configured size limits.
Location is used for placing content on maps, discovering nearby devices, and providing context for events and alerts. Location sharing can be disabled entirely, and granularity can be controlled from the Security settings panel.
Location detection varies by platform:
If no location source is available, the app falls back to the location stored in your profile settings.
lib/services/user_location_service.dartWhen location sharing is enabled, your coordinates (subject to your granularity setting) are visible to:
/api/status responseThe Security settings panel provides a location granularity slider that controls how precisely your coordinates are shared. The SecurityService.applyLocationGranularity() method rounds coordinates before they are transmitted:
Default is 50 km (city level). The Tracker app also stores proximity history locally only.
lib/services/security_service.dartGeogram uses the NOSTR protocol for identity, message signing, and relay communication. All identity is based on public-key cryptography, with no accounts, emails, or usernames required.
Geogram uses secp256k1 elliptic curve cryptography (the same curve used by Bitcoin). Key generation produces:
Keys are generated locally on your device. The private key never leaves your device and is never transmitted over any network.
lib/util/nostr_crypto.dart · lib/util/nostr_key_generator.dartEvery message, event, and reaction is signed with a 64-byte Schnorr signature (BIP-340) using your private key. This provides:
On the web platform, signing can optionally use a NIP-07 browser extension, allowing the private key to never be stored in the browser.
lib/services/signing_service.dartThe private key is stored in your profile as an nsec (bech32-encoded). When encrypted storage is enabled, the key is protected at rest using AES-256-GCM with a key derived via HKDF.
The public key (npub) is freely shared during HELLO handshakes, NOSTR events, and discovery responses. It is used by other devices to verify your signatures.
Geogram's NOSTR relay implementation supports:
The relay stores events in a local SQLite database (nostr/relay.sqlite3).
Different transports provide different levels of encryption:
Regardless of transport encryption, all messages carry NOSTR Schnorr signatures that guarantee authenticity and integrity end-to-end.
The Security tab in Geogram's settings provides user-facing controls for privacy and security. These options are accessible from the app's settings screen on all platforms.
A toggle that restricts all connectivity to Bluetooth Low Energy only, completely disabling internet and LAN transports. Useful for maximum privacy when you only want to communicate with physically nearby devices.
Marked as Restrictive in the UI to indicate its impact on connectivity.
Two toggles control API exposure:
A slider (0.0 to 1.0) that controls how precisely your location is shared with others. The SecurityService applies this granularity by rounding coordinates before they are transmitted.
The UI displays the current precision (e.g. "2.5 km") with a visual privacy level indicator: Precise, Neighborhood, City, or Region.
A toggle to enable full-profile encryption using AES-256-GCM. Requires an active nsec (private key). When toggled, a real-time progress indicator shows files processed, total files, and the current file being encrypted or decrypted.
A confirmation dialog is presented before enabling or disabling, as the operation processes all profile files.
Displays and allows changing the storage path for all Geogram data (desktop and mobile only). When changed, all files are automatically migrated to the new location. Includes copy-to-clipboard and open-in-file-manager options.
Available from the Profile settings page (separate from Security tab):
Geogram makes no network requests you didn't initiate.
There are no analytics, no telemetry, no crash reporting services, no update checks to external servers, and no third-party API calls baked into the application.
The only outbound connections are:
Everything else — AI inference, speech recognition, geolocation, map rendering — runs locally on your device using bundled models and databases.