davi-nfc-agent

command module
v1.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 39 Imported by: 0

README

Davi NFC Agent

A lightweight NFC card reader agent with WebSocket broadcasting capabilities. Reads and writes NDEF formatted data from NFC tags and broadcasts to connected clients in real-time. This is for use for the NFC-related functionality integrated into the Davi platform.

Features

  • Multiple Device Support: Hardware NFC readers and remote devices simultaneously
  • Remote NFC Devices: Smartphones, browsers with WebNFC, or any device that can connect to the API
  • Rich NDEF Read/Write: Text, URI, smart poster, vCard, MIME, geo, tel/sms/mailto, Android Application Records, and fully custom raw records
  • Reliable Writes: Read-after-write verification, automatic retry on transient failures, and pre-flight capacity checks
  • Tag Locking & Erase: Make tags read-only or wipe them back to an empty NDEF message
  • Tag Capabilities: Memory size, usable capacity, and write/lock/password support reported with every scan
  • Real-time WebSocket: Instant tag data broadcasting
  • Secure by Default: Automatic TLS (WSS) with key pinning for devices, an origin allowlist for browsers, plus optional API-secret authentication
  • Per-device Pairing: each device gets its own revocable credential, issued against a PIN
  • Auto-discovery: mDNS/Bonjour advertising for zero-config device setup
  • Cross-platform: Linux, macOS, Windows
  • System Tray UI: Device management and status
  • Control Center: A built-in web console for logs, tag inspection, NDEF writing, per-device revocation and persistent settings

Supported Devices

Hardware Readers: ACR122U, ACR1252U, and other PC/SC-compatible readers

Remote Devices: Any NFC-capable device that connects via the Device API, including:

  • Smartphones (iPhone 7+/iOS 13+, Android 4.4+)
  • Browsers with WebNFC (Chrome on Android)
  • Custom hardware or IoT devices

Card Types: MIFARE Classic (incl. NDEF formatting and custom keys), DESFire, Ultralight, NTAG21x, ISO14443-4 Type 4A (experimental)

Quick Start

Download pre-built binaries from releases, or build from source:

git clone https://github.com/dotside-studios/davi-nfc-agent.git
cd davi-nfc-agent
go build .
./davi-nfc-agent

See the Installation Guide for platform-specific setup and troubleshooting, and Setting up an iOS or Android device for pairing a phone.

Control Center

Choose Open Control Center from the tray menu to manage the agent in a browser: read its log, inspect and write tags, revoke a single paired device, edit the origin allowlist, and set preferences that survive a restart.

It is reachable only over loopback, only from a page the agent served, and only with a token minted by that tray entry — the origin allowlist plays no part in it. See Control Center.

It is a self-contained package (webui/, frontend included) that reaches the agent through one interface, so go build -tags nowebui . omits the routes, the privileged API and the embedded console without touching anything else.

Command-line Options
./davi-nfc-agent                       # System tray mode (default)
./davi-nfc-agent -version              # Print version information and exit
./davi-nfc-agent -device "ACS ACR122U" # Use a specific PC/SC reader by name
./davi-nfc-agent -device-port 9480     # Custom agent server port (default 9470, serves both devices and clients)
./davi-nfc-agent -api-secret mysecret  # Set the API authentication secret
./davi-nfc-agent -allowed-origins app.example.com  # Let a hosted web console connect
./davi-nfc-agent -require-paired-devices  # Admit only devices that have paired
./davi-nfc-agent -install-ca           # Trust this agent in browsers (installs a local CA)
./davi-nfc-agent -auto-tls=false       # Disable automatic TLS certificate management
./davi-nfc-agent -cert cert.pem -key key.pem  # Use your own TLS certificate
./davi-nfc-agent -config-dir ./config  # Override the config directory
Connecting from a web console

The agent only accepts WebSocket upgrades whose Origin matches its own host:port — otherwise any site the operator visits could drive the reader, including permanently locking cards. A console served from anywhere else, which is every hosted one, must be allowed.

The Davi consoles are allowed out of the box, so nothing needs configuring for them. The allowlist lives in allowed-origins.json in the config directory and is managed from the tray under Allowed Origins, which lists what is permitted and lets you revoke any of it.

When a page is refused, the tray offers it. The blocked origin appears as "Allow example.com" — one click admits it and persists the choice, no restart. That is the intended way to add a console.

To preload one instead, at first run or for an unattended install:

./davi-nfc-agent -allowed-origins "console.example.com,localhost:3002"
# or
DAVI_NFC_ALLOWED_ORIGINS="console.example.com" ./davi-nfc-agent

Entries are matched on host:port. Full URLs are accepted and reduced, so https://console.example.com and console.example.com are equivalent.

Allow any origin (this session) in the tray turns the check off until the agent restarts. It is deliberately never persisted, and it is not a way to skip configuring an origin — while it is on, any page the operator opens can read, write and permanently lock cards.

A trusted certificate is a separate requirement. The origin allowlist decides who may connect; TLS decides whether the browser will open the connection at all. A wss:// connection to an untrusted certificate fails outright — unlike a page visit, there is no warning to click through. See How devices trust the agent.

How devices trust the agent

By default the agent serves a self-signed certificate using a key it generates once and keeps. Nothing is added to any trust store.

Phones, readers and other native clients authenticate the agent by pinning its public key rather than by trusting an authority. The pin is reported in the registration response as serverInfo.publicKeyPin, logged at startup, and takes the form sha256/<base64> over the SubjectPublicKeyInfo:

Agent public key pin: sha256/47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=

Record it when pairing and compare it on every later connection. It survives certificate reissues, which happen whenever the host's addresses change, so a device that pins it keeps working when the machine moves network. Pin this value, never the certificate.

Setting up an iOS or Android device covers the pairing flow and the trust-evaluation code for both platforms.

Browsers cannot pin, so they need a certificate they already trust. Three ways:

  1. Provide one — point -cert / -key at a certificate for a name you control that resolves to the agent. Nothing is installed, and the browser trusts it because a public CA issued it. This is the option that scales.
  2. Trust This Agent in Browsers — in the tray, or under Device trust in the Control Center. Creates a local certificate authority, installs it in the system trust store and reissues the agent's certificate under it. The operating system asks for a password, and the listeners restart so the new certificate is the one served. This is the same thing -install-ca does, without needing a terminal or a restart with flags.
  3. -install-ca — the launch-flag equivalent of option 2, for a machine provisioned by a script.

A certificate authority in a trust store can sign for any name, not just this agent. Whoever holds its key can intercept that machine's traffic, so option 1 is preferable wherever you can arrange it. An install that already has a CA keeps using it, so upgrading changes nothing for a console that works today.

By default the agent generates and persists a TLS certificate and an API secret under a platform-specific config directory, so paired devices keep working across restarts. Run ./davi-nfc-agent -help for the full list of flags.

Usage Examples

The agent runs a single server on one port that fills both roles, plus a bootstrap helper:

  • Agent Server (port 9470): Serves both NFC devices (readers and smartphones, via /ws?mode=device) and client applications (via /ws) on the same port. The port is configurable via -device-port.
  • CA Bootstrap Server (port 9472): Serves the root certificate for device setup, when a local CA is in use (-install-ca)
JavaScript / TypeScript

Use the included client library for browser or Node.js applications.

const client = new NFCClient('http://localhost:9470');

client.on('tagData', (data) => {
  console.log('Card:', data.uid, data.text);
});

await client.connect();

// Write to a card
await client.write({
  records: [{ type: 'text', content: 'Hello, NFC!' }]
});
Android (Kotlin)

Connect to the agent's client endpoint via WebSocket using OkHttp or similar.

val client = OkHttpClient()
val request = Request.Builder()
    .url("ws://192.168.1.100:9470/ws")
    .build()

val listener = object : WebSocketListener() {
    override fun onMessage(webSocket: WebSocket, text: String) {
        val msg = JSONObject(text)
        if (msg.getString("type") == "tagData") {
            val payload = msg.getJSONObject("payload")
            Log.d("NFC", "Card UID: ${payload.getString("uid")}")
        }
    }
}

client.newWebSocket(request, listener)

See API Reference for the full WebSocket protocol.

Use Your Phone as an NFC Reader

Connect your smartphone to the agent using the NFCDeviceClient.

const device = new NFCDeviceClient('ws://192.168.1.100:9470');

device.on('registered', ({ deviceID }) => {
  console.log('Registered as:', deviceID);
});

await device.connect();

// Start scanning with WebNFC (Chrome on Android)
if (NFCDeviceClient.isWebNFCSupported()) {
  await device.startNFCScanning();
}
Raw WebSocket

Connect directly without a client library. See API Reference for all message types.

const ws = new WebSocket('ws://localhost:9470/ws');

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'tagData') {
    console.log('Card UID:', msg.payload.uid);
  }
};

// Write request
ws.send(JSON.stringify({
  type: 'writeRequest',
  payload: {
    records: [{ type: 'text', content: 'Hello!' }]
  }
}));

Extending

The agent's modular NFC layer supports adding custom readers and tag types beyond the built-in PC/SC and smartphone support. See Extending NFC Support to integrate your own hardware or protocols.

Contributing

See CONTRIBUTING.md for development setup, cross-compilation, and guidelines.

License

MIT License


Copyright © 2025-2026 Ned Palacios and Dotside Studios. All rights reserved.

Documentation

Overview

Package main provides an NFC card reader agent with WebSocket broadcasting capabilities. It supports reading NDEF formatted text from Mifare Classic tags and broadcasts the data to connected WebSocket clients.

Directories

Path Synopsis
Package buildinfo contains application metadata that can be set at build time.
Package buildinfo contains application metadata that can be set at build time.
Package logbuf keeps the agent's most recent log output in memory so it can be read back after the fact, there being no stderr to read when the agent is started from a desktop launcher.
Package logbuf keeps the agent's most recent log output in memory so it can be read back after the fact, there being no stderr to read when the agent is started from a desktop launcher.
nfc
multimanager
Package multimanager provides a multi-manager that aggregates multiple NFC Manager implementations.
Package multimanager provides a multi-manager that aggregates multiple NFC Manager implementations.
nfctest
Package nfctest provides in-memory NFC tag emulators and a high-level façade for testing NFC code without hardware.
Package nfctest provides in-memory NFC tag emulators and a high-level façade for testing NFC code without hardware.
Package protocol provides NFC message types for external tools.
Package protocol provides NFC message types for external tools.
Package server provides shared server utilities.
Package server provides shared server utilities.
clientserver
Package clientserver provides the WebSocket server for client applications.
Package clientserver provides the WebSocket server for client applications.
deviceserver
Package deviceserver provides the WebSocket server for NFC readers and devices.
Package deviceserver provides the WebSocket server for NFC readers and devices.
unifiedserver
Package unifiedserver serves the device and client WebSocket endpoints from a single HTTP listener on one port.
Package unifiedserver serves the device and client WebSocket endpoints from a single HTTP listener on one port.
Package tls provides automatic TLS certificate management with cross-platform trust store installation.
Package tls provides automatic TLS certificate management with cross-platform trust store installation.
Package webui serves the agent's control center: a privileged HTTP API under /control and the console that drives it, whose source lives in frontend/ and whose build is embedded by embed.go.
Package webui serves the agent's control center: a privileged HTTP API under /control and the console that drives it, whose source lives in frontend/ and whose build is embedded by embed.go.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL