{"slug": "telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket", "title": "Telling devices apart without accounts: 576 animal + adjective names for a Socket.IO clipboard app", "summary": "A developer built Clipboard Share, a no-signup Socket.IO tool for moving files and text between devices on the same network, and solved the anonymous device-list problem with a server-side naming scheme that assigns each device a human-friendly tag like \"Sleepy Panda.\" The server stores no names, only two indexes across 24 adjectives and 24 animals (576 combinations), resolving conflicts by keeping the animal and changing the adjective, falling back to a numeric suffix when all combos are taken. Sender identity is stamped server-side with any client-supplied sender field discarded for anti-spoofing, and a reroll button with a 3-second cooldown lets users change their own tag.", "body_md": "When several devices share a room with no login, the device list has a problem: nobody knows which row is \"my laptop\" and who just sent that file. That came up while building Clipboard Share, a no-signup tool for moving files and text between devices on the same network. My fix was a small naming scheme: every device gets a tag like \"Sleepy Panda\". I built the app with Claude Code: [https://www.clipboardapp.org](https://www.clipboardapp.org)\n\nThe rooms themselves come from a hashed-IP design that I covered in an earlier post (in Korean). This one is about what happens inside the room.\n\nWith no account there is nothing to point at. With only a device type and a browser name, two phones of the same model look identical in the list, so the server gives each device a human-friendly label. The first implementation landed on 2026-10-01 in commit `a6ebd2b` (\"device identity (animal + adjective) assignment and reroll, sender stamping\").\n\nThe server never stores a name. It only handles two indexes: 24 adjectives times 24 animals, so 576 combinations.\n\n```\n/** 24 adjectives x 24 animals = 576 combos. Index order must match the frontend */\nexport const ADJ_COUNT = 24;\nexport const ANIMAL_COUNT = 24;\nexport const IDENTITY_COMBOS = ADJ_COUNT * ANIMAL_COUNT;\n```\n\nThe frontend turns indexes into text in the viewer's language through i18n keys (`identity.adj[n]`, `identity.animal[n]`). The same device is \"졸린 판다\" in Korean and \"Sleepy Panda\" in English.\n\nOn connect, the server collects the combos already used in the socket's rooms (the global room and the network room) and picks around them. The fallback order, from the source comments:\n\n```\n * 1) preferred combo (saved value) if it is free\n * 2) on conflict, keep the animal and change only the adjective\n * 3) otherwise any free combo\n * 4) if all 576 are taken, append a suffix number (2, 3, ...)\n```\n\nThe \"preferred\" combo is what the browser remembered. The client keeps it in `localStorage` under `clipboard-identity` for 30 days:\n\n``` js\nconst STORAGE_KEY = 'clipboard-identity'\nconst STORAGE_TTL_MS = 30 * 24 * 60 * 60 * 1000\n```\n\nThe server does not trust it. `sanitizeIdentity` checks that both values are integers in range and drops anything else.\n\nThe \"reroll\" button sits on your own row in `DeviceList.vue`. The server passes the current combo as `avoid`, so you never get the same name twice in a row, and each socket has a 3-second cooldown:\n\n``` js\nexport const REROLL_COOLDOWN_MS = 3000;\n...\nif (now - last < REROLL_COOLDOWN_MS) {\n    if (typeof ack === 'function') ack({ ok: false, error: 'cooldown', retryAfterMs: REROLL_COOLDOWN_MS - (now - last) });\n    return;\n}\n```\n\nAfter a reroll the server re-broadcasts `room-users` to both rooms, and the button stays disabled until `rerollAvailableAt`.\n\nWho sent a message is decided server-side. Any `sender` field from the client is thrown away and replaced:\n\n```\n/**\n * Attach the sender info to a publish message. The client's own sender field\n * is always discarded and overwritten (anti-spoofing).\n */\nexport const stampSender = (msg: unknown, device: DeviceInfo | undefined) => {\n```\n\nThe stamp carries `socketId`, `identity`, device label, browser, and model (` toSender`). The receiving side uses it to tell \"sent by me\" from \"sent by another device\".\n\nThe 24 animals are Microsoft Fluent Emoji (Flat) SVGs under the MIT license. The license text and source link live next to them in `frontend/src/assets/animals/LICENSE`. They are exported as separate hashed files (` import.meta.glob` with `?no-inline`), not inlined into the JS bundle.\n\n`senderName` in `useArrivalBadge.js`), falling back to model, browser, then a generic string. The arrival notifications PR (#74) merged today, 2026-11-12.`usePairFlow.js`).` routes/quickSend.ts`).\nOpen [https://www.clipboardapp.org](https://www.clipboardapp.org) on two devices and the names show up right away. If something feels off, there is a \"건의하기\" (feedback) link at the bottom of the site, and I would like to hear it.\n\nOriginally published in Korean on velog: [velog](https://velog.io/@ch_kang/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%86%EC%9D%B4-%EA%B8%B0%EA%B8%B0-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0-%EB%8F%99%EB%AC%BC%ED%98%95%EC%9A%A9%EC%82%AC-%EB%9E%9C%EB%8D%A4-%EB%8B%89%EB%84%A4%EC%9E%84-576%EA%B0%9C%EB%A1%9C-%EB%B3%B4%EB%82%B8-%EC%82%AC%EB%9E%8C-%ED%91%9C%EC%8B%9C-Socket.IO-%EC%98%A8%EB%9D%BC%EC%9D%B8-%ED%81%B4%EB%A6%BD%EB%B3%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EA%B8%B0)", "url": "https://wpnews.pro/news/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket", "canonical_source": "https://dev.to/kangchanghwan/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socketio-clipboard-app-458m", "published_at": "2026-11-11 23:04:56+00:00", "updated_at": "2026-11-11 23:22:31.416677+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Clipboard Share", "Socket.IO", "Claude Code", "Microsoft Fluent Emoji"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket", "markdown": "https://wpnews.pro/news/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket.md", "text": "https://wpnews.pro/news/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket.txt", "jsonld": "https://wpnews.pro/news/telling-devices-apart-without-accounts-576-animal-adjective-names-for-a-socket.jsonld"}}