An NFC card reader agent with WebSocket broadcasting. It reads and writes NDEF-formatted data from NFC tags and broadcasts it to connected clients in real time, and provides the NFC functionality used by the Davi platform.
main.go, in
any shape from the full tray application to a headless serviceHardware Readers: ACR122U, ACR1252U, and other PC/SC-compatible readers
Remote Devices: Any NFC-capable device that connects via the Device API, including:
Card Types: MIFARE Classic (incl. NDEF formatting and custom keys), DESFire (EV1, EV2 and EV3 named, memory size and NDEF capacity read off the card, files behind AES keys read and written over an authenticated session, EV2 or the EV1 exchange before it, and locking), Ultralight, NTAG21x, NTAG 424 DNA (NDEF read/write, and offline SDM/SUN tap verification), ISO14443-4 Type 4A (experimental), FeliCa (identifier only: its own command set is not implemented)
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 ./cmd/davi-nfc-agent
./davi-nfc-agent
See the Installation Guide for platform-specific setup and troubleshooting, Setting up an iOS or Android device for pairing a phone, and Control Center for the built-in web console.
./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 -allow-loopback-bypass # Admit connections from this host with no secret (off by default)
./davi-nfc-agent -allow-raw-apdu # Open the raw APDU channel so clients can send raw exchanges (off by default)
./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
The agent listens on two ports, both configurable:
/ws?mode=device, client
applications to /ws, devices pair at /pair, and the Control Center is
served from the same portUse 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!' }]
});
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.
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();
}
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!' }]
}
}));
The agent is a Go module, and the binary is an ordinary program built from the packages it exports. Leaving out the tray, the control center or the hardware backend is a matter of which packages you import, and the listener, the pairing server and the certificate are plugins a program registers. See Custom Builds.
go get github.com/dotside-studios/davi-nfc-agent
The NFC layer takes custom readers and tag types beyond the built-in PC/SC and smartphone backends. See Extending NFC Support to integrate your own hardware or protocols.
See CONTRIBUTING.md for development setup, cross-compilation, and guidelines.
Copyright © 2025-2026 Ned Palacios and Dotside Studios. All rights reserved.