Your phone knows a lot of things about itself, and it hands them out on wildly different terms. Some are free to any app that asks. Some cost you a permission prompt. Some need an Apple entitlement and a server. Some Apple sealed off years ago and no app will ever read again. And a handful — your UDID, your serial number, your IMEI — only your device can disclose, about itself, to you.
Specdeck is a catalog of all 70 of them, sorted by what it costs to ask. Every entry says what the identifier is, who else can see it, what resets it, and why it matters. The ring on the front counts how many are readable right now, and deliberately leaves out the ones Apple has closed off — grading your phone down for a platform decision would make the number meaningless.
The gaps are the interesting part. A MAC address that's been frozen since iOS 7, a carrier name removed in iOS 16, an IDFA that reads all zeros until you allow tracking. Specdeck shows those rows rather than hiding them, because why you can't have a value tells you more than the value would have.
The erase button is a demonstration, not a setting. Wipe the identifiers Specdeck created about your install, then watch what comes back: the install ID is brand new, the keychain ID is byte-for-byte identical, and IDFV doesn't move at all. Three identifiers that look alike in a list behave completely differently when you attack them. That difference is the whole subject of the app.
Consent is enforced by the compiler. SpecdeckAPI cannot be constructed
without a ConsentToken, and only ConsentStore can mint one — its initializer
is fileprivate. "No network request without consent" is a property the type
system checks, not a runtime if somebody eventually forgets to write.
Your UDID never touches a disk. The device signs its report with a
certificate whose subject contains the UDID. The server verifies that chain
in-process, holds the values in memory for minutes at most, and deletes them the
moment the app has read them. The logger is an allowlist — it emits only fields
whose names appear in LOGGABLE_FIELDS and silently drops everything else, so a
device identifier reaching docker logs isn't a mistake someone can make.
It tells you what your device actually did. Specdeck requests MEID, ICCID, IMSI and EID on every report knowing most come back empty, then shows you exactly which ones your hardware withheld. That's a finding about your phone, not a fixed answer copied out of documentation.
|
|
Does Specdeck send my identifiers anywhere?
No, and there's a counter on the Privacy tab proving it. Everything in the deck is read on-device. Two features can reach the network — asking a server what it sees about you, and the device report — and both are off until you turn them on, each behind its own consent.
Then how does the device report get my UDID?
Your device tells you. Apple removed the ability for apps to read the UDID in 2013 and nothing has brought it back. The report uses Apple's own Over-the-Air Profile Delivery mechanism: your device signs a report about itself and posts it to the server, which hands it straight back to you and deletes it. No app read anything — the device disclosed it.
Does it install anything on my phone?
No. And this is the strange part: iOS will tell you "Profile installation failed", and that message is the successful ending. Apple's enrollment protocol assumes a server wants to install something, and gives you no way to say "we're done, install nothing". So iOS reports a failure at the end of an exchange that worked perfectly. Your identifiers arrived before that dialog appeared, and Settings → General → VPN & Device Management stays empty. The alternative was installing something you didn't ask for, which seemed worse than explaining a dialog.
What if someone's looking over my shoulder, or I'm recording my screen?
Sensitive values need a deliberate press and hold and re-hide themselves afterwards. Reveal-all exists for when you actually want to read a dozen of them, and re-masks automatically after 60 seconds — so walking away with the app open isn't a mistake you can make either. Serial numbers, IMEIs and UDIDs reveal nothing at all until you ask: a partial IMEI is still a head start for SIM-swap fraud.
Do I need an account?
There isn't one. No sign-in, no email, no analytics, no crash reporting, no attribution SDK. There's no dashboard anywhere with your name on it because there's no dashboard.
Why does it ask permission to track me?
Because allowing tracking is the only way iOS will reveal your advertising identifier, and showing it to you is the point. Specdeck doesn't track you and sends nothing anywhere. Same story with location: iOS requires location access before any app can read the name of the Wi-Fi network you're on. Every unlock sheet quotes the system prompt word for word first, so the real dialog holds no surprises.
Can I export this?
As JSON or Markdown, at three redaction levels. Redacted drops sensitive values entirely and is safe to paste into a bug report. Hashed turns them into one-way hashes, so you can tell two of your own reports apart without disclosing anything. Everything is verbatim, and asks you to confirm first, because that file contains your serial number and IMEI in plain text.
Is it on the App Store?
Not yet. Build it from this repo, or run the device report in your browser at specdeck.walhallaa.com — it works on any iPhone in Safari, no app required.
SwiftUI on iOS 26, zero third-party packages. The server is Node 22 and Hono,
with node-forge and plist doing the PKCS#7 and property-list work — 27 tests,
no framework.
-
The catalog is data. All 70 identifiers are values in one registry file, so there's one row view, one detail view and one set of tests instead of seventy screens that drift apart. Localization keys are derived from identifier ids, which means a typo would silently render as its own raw key at runtime — so
tools/check-strings.mjscross-checks the 557 strings against the Swift sources in both directions before a build ever runs. -
"Not available" is never a blank cell. Every way of not having a value is a distinct case in one enum, carrying the reason and, where one exists, the action that changes it. A permission you can grant, a capability this build lacks, a door Apple closed, or a device that was asked and declined — four different answers where most apps show an empty string.
-
Two independent checks on the device's signature.
openssl smime -verifyconfirms the signature actually covers the content;node-forgeconfirms the chain reaches a pinned Apple root, in-process, because the leaf certificate's common name contains the UDID and nothing carrying that may touch disk. Certificate expiry is deliberately ignored — Apple's documentation requires it, since device certificates are routinely long expired and iOS keeps using them.
# iOS app — needs Xcode with the iOS 26 SDK
open specdeck.xcodeproj
# Profile service
cd server
npm install
npm test # 27 tests, no network required
npm run dev # http://localhost:8080The server needs a real HTTPS certificate to sign profiles — iOS shows a red
"Unverified" banner otherwise, which users can't tell apart from an actual
attack, so it refuses to serve an unsigned one rather than degrade quietly. See
server/DEPLOY.md.
The catalog is the easy place to start: add a line to
specdeck/Core/IdentifierRegistry.swift, add its strings to
specdeck/Resources/Localizable.xcstrings, and run node tools/check-strings.mjs
— it will tell you exactly which keys you missed, in either direction.
Deploying your own instance needs a Cloudflare API token and a hostname; nothing
else is secret. server/DEPLOY.md has the full walkthrough
and server/.env.example lists every variable.
MIT. Found a value Specdeck gets wrong, or an identifier it's missing — open an issue or email info@walhallaa.com.
Built by Murat Can Koç




