Privacy & Security

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.

GDPR & Data Deletion

Geogram's decentralized architecture means your data is primarily under your control. Here is how data rights and deletion work within the system.

Data Residency

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:

  • No data controller exists beyond the user themselves for locally-stored data
  • No personal data is collected by Geogram as an organization
  • No analytics, tracking, or telemetry data is gathered
  • No email address, phone number, or real name is required to use the software

The only personal identifier is a cryptographic keypair (npub/nsec) generated locally on the device.

Local Data Deletion

Users have full control over local data deletion:

  • Delete Profile: Removes the entire profile directory and all associated data (messages, events, settings, contacts). Available from Profile Management.
  • Delete Working Folder: The storage folder can be located and deleted directly from the filesystem.
  • Clear Crash Logs: Diagnostic logs can be cleared from the Security settings (Android).
  • Uninstall: Removing the application and deleting ~/.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.

Station-Side Data

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:

  • NOSTR NIP-09 deletion: You can publish a Kind 5 deletion event referencing the event IDs you want removed. Compliant relays will delete the referenced events.
  • Email cache expiry: Cached emails on stations auto-expire after 24 hours.
  • Blossom blob pruning: File storage is pruned oldest-first when the station exceeds its storage cap.
  • Station operator: For a full data purge, contact the station operator. Since stations are community-run (often by the user themselves), this is typically straightforward.

Right to Portability

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.

Identity Pseudonymity

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.

GDPR Considerations for Station Operators

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:

  • Implementing NIP-09 deletion event handling (supported by default in the relay)
  • Configuring reasonable Blossom storage limits and expiry policies
  • Setting email cache auto-expiry (default: 24 hours)
  • Documenting a privacy policy for users connecting to your station

For personal/family stations, GDPR's household exemption typically applies.

Connection Routing

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.

Optimal Path Selection

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.dart

Routing Strategies

Four routing strategies are available, with PriorityRoutingStrategy as the default:

  • Priority: Selects by transport priority number (lower = preferred). Falls back to all available if none are directly reachable.
  • Quality: Selects based on historical metrics including average latency and success rate per device.
  • Failover: Uses an explicit transport ordering with sequential fallback.
  • MessageType: Routes different message types (chat, files, API) to different transports.
lib/connection/routing_strategy.dart

Transport Priority Order

Each transport has a fixed priority value. Lower numbers are preferred:

  • Priority 5 — USB: Wired connection. Fastest, most reliable, zero interference.
  • Priority 10 — LAN: Direct HTTP on local network. Low latency.
  • Priority 15 — WebRTC: Direct P2P over internet. No relay bandwidth cost.
  • Priority 30 — Station: Relayed through station WebSocket. Works when P2P fails.
  • Priority 35 — Bluetooth Classic: SPP profile for legacy devices.
  • Priority 40 — BLE: Bluetooth Low Energy. Lowest bandwidth, works fully offline.

Message ID tracking ensures no duplicates are delivered when a message is attempted across multiple transports.

Internet

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.

Station Servers

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.dart

Connection Frequency

Once 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.dart

NOSTR Relay Authentication

Station 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.dart

Blossom File Storage

Stations 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.dart

Local Network (LAN)

On 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.

Network Scanning

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.dart

Data Shared on LAN

When a device responds to a discovery scan at /api/status, it returns:

  • Callsign (APRS-style identifier, e.g. "X3ABCD")
  • Device type: station, desktop, client, or unknown
  • Device name, software version
  • Connected device count
  • Latitude and longitude (if location sharing is enabled)

No authentication is required for discovery reads.

lib/connection/transports/lan_transport.dart

Bluetooth Low Energy (BLE)

BLE 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.

BLE Discovery

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:

  • Write characteristic (HELLO): 0000fff1-...
  • Notify characteristic (HELLO_ACK): 0000fff2-...
lib/services/ble_discovery_service.dart

HELLO Handshake Data

The HELLO event is a NOSTR-signed message containing identity information. The following data is transmitted during the handshake:

  • Callsign (device identifier)
  • npub (NOSTR public key)
  • Nickname (user display name)
  • Latitude and longitude (if available)
  • Device capabilities (hello, data, broadcast, chat, compression)
  • Classic Bluetooth MAC address (Android only, for BLE+ pairing)

The HELLO event is cryptographically signed, preventing identity spoofing.

lib/services/ble_message_service.dart

Platform Support

BLE message service supports data transfer, broadcast messaging, chat, and deflate compression. Messages larger than the BLE MTU are chunked automatically.

  • Full support Android, iOS (GATT server + client)
  • Client only Linux, macOS, Windows
  • Not supported Web browsers
lib/connection/transports/ble_transport.dart

Device Identification

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.

USB-C (Wired)

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.

Android Open Accessory Protocol

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.dart

Performance & Reliability

USB 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.dart

Platform Support

  • Full support Android (accessory mode via UsbManager)
  • Full support Linux (host mode via pure Dart FFI with libc ioctl)
  • Not supported iOS, Web, Windows

On 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.

lib/services/usb_aoa_linux.dart · android/.../UsbAoaPlugin.kt

Security Characteristics

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.md

WebRTC

WebRTC 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.

How WebRTC Is Used

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.

lib/connection/transports/webrtc_transport.dart

Signaling Process

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 teardown

Signaling messages are wrapped in NOSTR events. Once the P2P connection is established, data flows directly between devices without passing through the station.

Self-Hosted STUN Server

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.dart

Connection Timeouts

WebRTC connections are established with strict timeouts to prevent hanging:

  • ICE gathering timeout: 5 seconds
  • Connection establishment: 15 seconds
  • Offer response: 10 seconds

Trickle ICE is used (candidates sent as gathered) for faster connection establishment.

Server-Side Storage

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.

Preferred Station

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.

lib/services/station_service.dart

Email Relay & Storage

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.

lib/services/email_relay_service.dart · lib/services/email_service.dart

Alerts & Places

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.dart

Sync Flow

The general pattern for all user-contributed data is:

  • Content is created and stored locally on your device first
  • The content is signed with your NOSTR private key
  • The signed event is published to your preferred station
  • The station stores it in its database and notifies subscribed clients
  • For offline recipients, the station queues delivery for when they reconnect

You always retain your local copy. Station storage is a relay — it distributes your content but does not hold the only copy.

Cache & Local Storage

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.

Storage Layout

All data lives under ~/.local/share/geogram/:

  • profiles/{callsign}/ - Per-profile data (events, places, blogs, contacts, settings)
  • chat/ - Chat messages, shared across profiles
  • station/ - Station relay data when running as a server

Each app stores its data in human-readable formats (mostly NOSTR event JSON) within the profile directory.

lib/services/storage_config.dart

Encrypted Archive

Sensitive 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.

lib/services/encrypted_storage_service.dart

Message Cache

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).

lib/services/direct_message_service.dart

Map Tiles & Media

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.

Geolocation

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.

When Location Is Requested

Location detection varies by platform:

  • Android/iOS: Device GPS/location services (continuous when the app is active)
  • Windows/Linux: IP-based geolocation using an offline DB-IP database, updated every 10 minutes. No external queries are made.
  • Web: Browser Geolocation API (requires explicit permission)

If no location source is available, the app falls back to the location stored in your profile settings.

lib/services/user_location_service.dart

Who Sees Your Location

When location sharing is enabled, your coordinates (subject to your granularity setting) are visible to:

  • LAN devices: Any device scanning the local network receives location in the /api/status response
  • BLE devices: Any device within Bluetooth range receives location in the HELLO handshake
  • Connected stations: Station relays receive location if shared during HELLO
  • NOSTR relay clients: Location embedded in events is visible to all relay-connected clients

Location Granularity

The 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:

  • Precise (<50m) Street-level accuracy
  • Neighborhood (50m - 5km) Block-level
  • City (5km - 40km) City-level only
  • Region (>40km) Regional level

Default is 50 km (city level). The Tracker app also stores proximity history locally only.

lib/services/security_service.dart

Encryption & NOSTR

Geogram 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.

Key Generation & Curve

Geogram uses secp256k1 elliptic curve cryptography (the same curve used by Bitcoin). Key generation produces:

  • Private key (nsec): 256-bit random value, bech32-encoded for display
  • Public key (npub): X-only public key derived from the private key, bech32-encoded

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.dart

Message Signing

Every message, event, and reaction is signed with a 64-byte Schnorr signature (BIP-340) using your private key. This provides:

  • Authentication: Proof that the message came from the claimed sender
  • Integrity: Any modification to the content invalidates the signature
  • Non-repudiation: Sender cannot deny having created the message

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.dart

Private Key Storage

The 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.

lib/util/backup_encryption.dart

Supported NIPs

Geogram's NOSTR relay implementation supports:

  • NIP-01: Core protocol (EVENT, REQ, CLOSE, EOSE)
  • NIP-09: Event deletion (Kind 5)
  • NIP-16: Replaceable and parameterized replaceable events
  • NIP-25: Reactions (Kind 7)
  • NIP-42: Authentication for relay writes

The relay stores events in a local SQLite database (nostr/relay.sqlite3).

NOSTR.md

Transport Encryption Summary

Different transports provide different levels of encryption:

  • USB: Physically private (wired). No over-the-air interception possible.
  • WebRTC: DTLS encryption on the data channel (encrypted in transit).
  • Station (WSS): TLS encryption when using wss:// URLs.
  • LAN: No transport encryption (relies on NOSTR signatures for integrity).
  • BLE: BLE link-layer encryption when paired, plus NOSTR signatures.
  • Local storage: Optional AES-256-GCM encryption for the archive.

Regardless of transport encryption, all messages carry NOSTR Schnorr signatures that guarantee authenticity and integrity end-to-end.

Security Settings Panel

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.

BLE-Only Mode

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.

API Access Controls

Two toggles control API exposure:

  • HTTP API: Enables/disables the local HTTP API server. When enabled, shows the local IP address and port for external access.
  • Debug API: Enables advanced debugging endpoints. Marked as Advanced.

Location Granularity

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.

Encrypted Storage

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.

Working Folder

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.

Identity & Profile Management

Available from the Profile settings page (separate from Security tab):

  • View npub/nsec: Copy your public and private keys. The nsec is displayed with a red warning label.
  • Reset Identity: Regenerates a new keypair with a confirmation dialog.
  • Delete Profile: Removes entire profile and all associated data (with confirmation; the last profile cannot be deleted).
  • Export/Import: Export all or single profiles to a file for backup or migration.
lib/pages/security_settings_page.dart · lib/pages/profile_management_page.dart

No External Calls

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:

  • Stations you explicitly connect to (your preferred station or stations you visit)
  • Optional GitHub API calls for release information on the downloads page of this website

Everything else — AI inference, speech recognition, geolocation, map rendering — runs locally on your device using bundled models and databases.