d0d6ae95f1
Folded into the existing on-demand Deep check button: queries Fingerbank's interrogate API with the device's MAC plus the SSDP SERVER header when deep-check-device.sh finds one, showing the confidence band alongside the result. Runs directly from the API container (no host-level access needed, just an outbound HTTPS call), unlike the SSDP/mDNS steps. Confirmed via direct testing: without DHCP fingerprint data (which we structurally don't have, not being the DHCP server), MAC-only queries often can't get past manufacturer-level confidence -- same info the free OUI lookup already provides. Documented honestly in docs/device-discovery.md rather than overselling it. Still worth having as opt-in enrichment for devices that do expose richer signals. Gated behind optional FINGERBANK_API_KEY -- missing key, API errors, or no match all degrade gracefully without affecting the rest of deep-check's local findings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
export interface FingerbankResult {
|
|
deviceName: string | null;
|
|
score: number | null;
|
|
version: string | null;
|
|
}
|
|
|
|
export interface FingerbankSignals {
|
|
mac: string;
|
|
// "Upnp string" per Fingerbank's docs -- we pass the SSDP SERVER header
|
|
// captured by deep-check-device.sh, when present (e.g.
|
|
// "Linux/3.10 UPnP/1.0 MiniDLNA/1.1"), as extra fingerprinting signal
|
|
// beyond the bare MAC.
|
|
upnpServer: string | null;
|
|
}
|
|
|
|
// On-demand only (folded into the existing deep-check button), never
|
|
// automatic/bulk -- keeps this well under Fingerbank's free-tier query
|
|
// limits and avoids sending every device's MAC to a third party by default.
|
|
export async function identifyDevice(
|
|
apiKey: string,
|
|
signals: FingerbankSignals
|
|
): Promise<FingerbankResult | null> {
|
|
const body: Record<string, unknown> = { mac: signals.mac };
|
|
if (signals.upnpServer) body.upnp_user_agents = [signals.upnpServer];
|
|
|
|
const res = await fetch("https://api.fingerbank.org/api/v2/combinations/interrogate", {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
if (res.status === 404) {
|
|
// No match in Fingerbank's database -- not an error, just nothing found.
|
|
return null;
|
|
}
|
|
if (!res.ok) {
|
|
throw new Error(`Fingerbank API returned ${res.status}`);
|
|
}
|
|
|
|
const data = (await res.json()) as {
|
|
device_name?: string;
|
|
score?: number;
|
|
version?: string;
|
|
};
|
|
|
|
return {
|
|
deviceName: data.device_name ?? null,
|
|
score: data.score ?? null,
|
|
version: data.version ?? null,
|
|
};
|
|
}
|