Skip to content
TDACorpIDENTITYSign in

Security practices

What TDACorp Identity does with your credentials, your sessions, your tokens and your second factors, with every number taken from the code that enforces it. It describes behaviour that exists in pre-production today, not a roadmap, and it lists at length what is not claimed.

Last reviewed October 5, 2026
On this page

Scope and status

This page describes the security behaviour of TDACorp Identity, the shared sign-in, session, token and authorization service operated by TDACorp Private Limited (an Indian company, incorporated 17 July 2026). It covers the sign-in and account pages, the OpenID Connect and OAuth 2.0 provider endpoints, the account API, the administrative API and console, and the background jobs that serve them.

The platform is in pre-production. Where a protection is built and running, the page says so in the present tense. Where a protection is designed but not built, or is built and switched off, the page says that instead and repeats it in the final section. Nothing here is a statement about a product built on top of this platform: such a product enforces its own authorization in its own database and has its own notice.

ItemStatement
OperatorTDACorp Private Limited
Last reviewed5 October 2026
LifecyclePre-production. Numbers below are what the code enforces today, not launch commitments
Evidence standardEvery figure is read from the constant or setting that enforces it, and a test fails when the page and the code drift apart
Independent assuranceNone. No outside party has audited or certified this platform
Security contactsecurity@tdacorp.in (also published in the machine-readable security.txt file)

Note

Session lifetimes, the step-up window and the other limits quoted on this page are the defaults every account receives today. Per-organization session lifetime, concurrent-session, minimum-assurance and allowed-method settings are read at sign-in for organization-scoped sign-ins, and the page quotes the defaults. The account pages use a fixed 30-minute freshness for second-factor checks.

Threat model summary

A written threat model exists in the repository, with numbered threats, the mitigation for each, the residual risk and the blast radius. The table below summarises it in plain words. Status uses three words only: Mitigated (the control is built and tested), Partly mitigated (built, with a stated gap) and Open (a known gap with no control yet).

ThreatWhat stops itStatus
Learning whether an address has an accountSign-in always performs one Argon2id comparison, real or against a dummy hash; registration, magic-link and recovery answer identically for known and unknown addresses; email-sending endpoints wait out a response-time floorPartly mitigated: the floor hides differences smaller than itself; the timing of other endpoints is not measured
Credential stuffing and password guessingRate limits on sign-in attempts, counted in the databasePartly mitigated: attempts are throttled and there is no permanent account lockout; the owner is emailed when repeated failed attempts hit the account
Weak or breached passwordLength and local weak-password rules plus a Have I Been Pwned range checkPartly mitigated: the breach check depends on an external service and is best effort
Stolen session cookieHttpOnly, Secure, host-locked cookie; server-side session lookup on every request; short idle and absolute lifetimes; step-up for durable changesPartly mitigated: a stolen live session can still read and revoke
Stolen refresh tokenRotation on every use; reuse of a retired token revokes the whole family and sessionMitigated, with a 10-second retry grace that a thief could exploit if they replay inside it
Stolen access token replayed from another machineSender-constraint through DPoP for every client except first-party web clients; 15-minute ceilingMitigated for constrained clients
Replay of a link or codeSingle-use claims done as one conditional statement; magic and recovery links bound to the requesting browser; GET requests consume nothingMitigated
Authorization-code interception60-second code, PKCE, exact redirect URI, atomic single redemptionMitigated
Cross-site request forgerySameSite=Lax cookies plus a same-origin check on every state-changing requestMitigated, relying on every supported browser sending an Origin or Sec-Fetch-Site header
Cross-site scripting and framingPer-response nonce Content-Security-Policy, no inline styles, frame-ancestors nonePartly mitigated: browser behaviour of the policy is recorded from the specification more than from browser tests
Hijacked session minting durable accessStep-up with a passkey or authenticator code for credential, export, token and device-trust changesMitigated
Cloned passkey authenticatorSignature counter must advance; refusal and audit event otherwisePartly mitigated: synced passkeys keep a zero counter and cannot be checked this way
Database read-only compromisePasswords hashed with Argon2id; session, refresh and token secrets stored as digests; recoverable secrets sealed with AES-256-GCMPartly mitigated: the key-wrapping key is held by a software provider in pre-production
Database plus key compromiseDesigned for a real KMSOpen in pre-production: see the cryptography section
Audit log tamperingAppend-only at the database, rows chained by hash, signed anchorsPartly mitigated: anchoring is not scheduled in the committed environment files
Cross-organization data accessRow-level security keyed on the transaction's organizationMitigated, with the platform role bypassing it by design under an audit guard
Webhook delivery used as a server-side request forgery vectorBlocked address ranges, resolved-address check, https only, per-send deadlinesPartly mitigated: the IPv4 side is a deny-list
Forged forwarded-address headers evading per-address limitsOne resolver determines the client addressOpen until verified against the real edge: the resolution has not been measured on a live deployment
Outbound email used up by an attackerCaps on outbound account emailPartly mitigated: the caps are best effort under concurrent requests
Compromised dependencyInstall-time and CI supply-chain gatesPartly mitigated: a compromised maintainer of a pinned dependency is accepted risk

Authentication factors compared

Passwords, passkeys, authenticator codes, recovery codes, magic links and federated sign-in each offer a different level of protection. The assurance level (AAL) follows the NIST SP 800-63B vocabulary used by the platform specification.

FactorTypical assurancePhishing resistantHow it is storedLimits and notes
Passkey (WebAuthn)AAL2 when user verification is performed, which the platform requiresYes: bound to the site's relying-party identityPublic key only; the private key never leaves your authenticatorChallenges are 32 random bytes, valid 5 minutes, single use. Accepted algorithms: EdDSA, ES256, RS256. Residential key and user verification are required
PasswordAAL1 on its ownNoArgon2id hash with a random salt and recorded parameters12 to 256 characters after Unicode normalization. Checked against a breach corpus
Authenticator app (TOTP)Second factor, giving AAL2 with a passwordNoSecret sealed with AES-256-GCM under a purpose-specific data keyRFC 6238: SHA-1, 6 digits, 30-second step, one step either side, 20-byte secret. A matched time step is recorded and never accepted again
Recovery codesBack-up for an authenticator app and an input to recoveryNoHMAC-SHA-256 keyed hash under a secret that is itself sealedBatches of 10, single use, 10 characters of base32 each; a new batch supersedes the old one
Magic link by emailAAL1NoSHA-256 digest of the link secretValid 15 minutes, single use, completes only in the browser that asked for it
Federated sign-in (Google, GitHub)AAL1 unless the provider asserts multi-factor from a trusted providerDepends on the providerA stored issuer and subject pair; no provider password is heldExists in code but offered only if an operator registers a provider; the subprocessor page lists none today. Does not auto-link by email match unless an operator turns on an off-by-default two-level opt-in (a platform flag and an organization setting) and the address is verified on both sides; the owner is emailed when it happens
Step-up (re-proof of presence)Raises the session's assurance for a windowPasskey: yes. Authenticator code: noEvidence row with an expiryWindow 10 minutes by default; see the step-up section

Note

No SMS or voice code is offered as a factor. The platform specification excludes SMS because of SIM-swap risk.

Password handling

How a password is stored

A password is put through Argon2id with parameters pinned in this platform's own code rather than left to a library default, so a routine dependency upgrade cannot quietly weaken them. The parameters are recorded with each hash, and an account's hash is upgraded to the current parameters the next time its owner signs in successfully.

ParameterValueNotes
AlgorithmArgon2idMemory-hard, resistant to GPU guessing
Memory64 MiBPinned in code
Passes (time cost)3Pinned in code
Lanes (parallelism)4Pinned in code
Salt16 random bytesNew for each hash
Stored formPHC stringParameters travel with the hash
ComparisonConstant timeAn unknown identifier still runs one full hash comparison against a fixed dummy hash
Imported legacy hashesA bcrypt-shaped hash is never verifiedThe attempt spends the same dummy work, answers like a wrong password, and is audited; the owner must set a new password through an emailed link or recovery

What a new password must satisfy

The same policy applies when you register, set or change a password, accept an invitation, or finish recovery. It is deliberately small: it avoids composition rules and refuses only what no person would call a password.

RuleValueApplies to
Minimum length12 characters after Unicode normalization (NFC)Every surface that lets you choose a password
Maximum length256 characters; a raw field over 1,024 is refused before normalization, never truncatedEvery such surface
Distinct charactersAt least 4 distinct code pointsEvery such surface
Repeated unitA password made only of one repeated unit of up to 4 characters is refusedEvery such surface
WalksA whole-string alphabet or keyboard-row run (for example a run along a keyboard row) is refusedEvery such surface
Common choicesA short list of the commonest twelve-plus-character passwords is refusedEvery such surface
Not applied at sign-inAn older imported password of 8 to 11 characters can still be verifiedSign-in only
Breach checkRefused if found in the Have I Been Pwned corpusSee below

Breached-password checking

When you register, set or change a password, accept an invitation, or finish recovery, the new password is checked against the Have I Been Pwned corpus using its range API. Only the first five hexadecimal characters of a SHA-1 digest of the password leave this system, with a padded response so its size reveals nothing. The comparison happens here, and no account identifier or email address is sent.

BehaviourDetail
What leaves the systemA five-character prefix of a SHA-1 digest, never the password, the full digest or an identifier
Re-checkEvery successful password sign-in re-runs the check in the background; a breached verdict writes an audit event that your Activity page shows with a prompt to change the password. It never delays the sign-in
Stored at restNeither the password, its hash nor the prefix is logged or stored by this check

Sessions and cookies

A session is a server-side record, referenced by an opaque cookie. Only a SHA-256 digest of the cookie value is stored. Every request to the account pages looks the session up in the database rather than trusting the cookie alone, so revoking a session, or deactivating an account, takes effect on the next request.

CookiePurposeLifetimeAttributes
__Host-tdaid_sidYour signed-in sessionSent for the session's absolute lifetime; the server ends it earlier on idle expiry or revocationHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-tdaid_ltxA sign-in in progress10 minutesHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-tdaid_rdevBinds a recovery link to the browser that asked30 daysHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-device_idLets the account recognise a browser it has seen; a random value, not personal data400 daysHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-tdaid_regb and __Host-tdaid_regvBind a registration code and a verified link to the registering browser30 minutes for the first, 15 minutes for the secondHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-tdaid_fedState for a federated sign-in in progress10 minutesHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
__Host-tdaid_pk_offerRemembers that a passkey offer was shown so it is not repeated at every sign-in7 daysHttpOnly, Secure, SameSite=Lax, Path=/, no Domain
Admin console session cookieThe administrative console's own session, a separate projectUp to 7 days as an upper bound; the access token and refresh token lifetimes gate real accessEncrypted (AES-256-GCM, direct key) rather than signed in plain text; host-locked in production. A second encrypted cookie holds a console sign-in in progress for 10 minutes

The __Host- prefix makes a browser refuse the cookie unless it is Secure, has path / and has no Domain attribute, so a sibling subdomain cannot set or overwrite it.

Session lifetimes and limits

PropertyValueApplies to
Idle timeout12 hoursDefault
Absolute lifetime14 daysDefault
Platform bounds in the specificationIdle at most 24 hours, absolute at most 30 daysDesign bounds
Rotation of the cookie secretOn privilege change: login completion, step-up and organization switchSession cookie
Login transaction10 minutesSign-in in progress
Remember-meOff unless an operator enables a flag; when on it is bounded by the configured session days and a ceilingSign-in
Device binding of the sessionOff unless an operator enables a flagSession continuity
Concurrent sessionsNot limited by defaultAccount

What ends sessions

EventEffect on sessions
You revoke one sessionThat session and its token family end
You choose to sign out everywhereEvery session, this one included
Password change, passkey removal, authenticator resetEvery other session and its refresh tokens end, outstanding sign-in links and open recovery requests are cancelled, and the session that made the change survives with a rotated cookie secret so a copy of the old cookie stops working. Personal access tokens are revoked only when the flow says so
Reverting a primary email change from the old addressSigns everything out
You choose "No, sign that session out" on a sign-in alertThe alerted session and every session the same browser opened since end; that device is forgotten and every other device drops from trusted to known
You choose "No, secure my account" on a sign-in alertEvery session, refresh family and personal access token ends; devices are forgotten; pending recovery requests and outstanding sign-in links are cancelled; you are emailed
Account deactivationEvery session, token and personal access token is revoked at once
A principal or organization is suspendedThe session is refused on the next request, and an introspecting resource server sees the token as inactive
Back-channel logoutRegistered applications are told the session ended

Tokens issued to applications

TokenLifetimeBinding and handling
Access tokenAt most 15 minutes (a hard ceiling that a client's own setting can only lower)Carries exactly one audience; bound to a key held by the client (DPoP, or mutual TLS where an operator enables it) unless the client is a first-party web application
Refresh token, first-party clientSliding window set per client, never beyond the session's absolute expiryOpaque, stored by hash; rotates on every use
Refresh token, third-party client7 days sliding and 30 days absolute at mostSame rotation; the absolute limit never exceeds the session's
Authorization code60 secondsStored as a digest, requires the PKCE verifier and the exact registered redirect URI, redeemed atomically; a second redemption revokes the family the first created
Pushed authorization request60 secondsSingle use
Client credentials token (service clients)15 minutes, no refreshSender-constrained
Back-channel logout token15 minutesSigned, delivered through a retrying outbox
Personal access tokenUp to 90 days, never longerFormat tdaid_pat_; shown once, stored only as a hash; can carry an IP allowlist; creating or revoking needs step-up
Client assertion (private key JWT)At most 5 minutesSigned by the client's registered key
DPoP proofIssued-at time accepted within 60 seconds either side of the server clock; the jti is remembered for 5 minutesA server-supplied nonce is required
Signing keyRotated on a 30-day period by a scheduled job, with an overlap of 15 minutes plus 1 day before a retired key leaves the key setAlgorithm EdDSA (Ed25519); ES256 optional per client

Rotation and reuse detection

Refresh tokens rotate on every use, and a token that was already used is remembered. If a retired token is presented again after a 10-second retry window, that is the signature of a stolen copy being replayed: the whole token family and its session are revoked, the account's authorization version is bumped so tokens already issued stop being accepted by anything that checks, and the event is audited. The short window exists only for the legitimate case of a client retrying after a dropped response. The sender-constraint check is applied after reuse detection, so a stolen token presented under the thief's key still counts as reuse.

After a reuse, a background job also ends every other session, refresh token family and personal access token of the account (only those created before the reuse) and emails the account owner that a reused sign-in token ended their access as a precaution. That email is a security notice that cannot be switched off.

Step-up re-authentication

A session is a bearer credential: anything that can replay it speaks with your full authority while it lives. So the operations that would turn a short-lived theft into durable access ask you to prove presence again with a passkey or an authenticator code (or, for an account with a linked provider, a sign-in through that provider).

PropertyValue
Default step-up window10 minutes
Bounds in the specification1 to 30 minutes per organization; the account pages apply a fixed 30-minute freshness, and the window setting is read only for organization administrator writes
Freshness required for second-factor operations on the account pagesThe session must be at assurance level 2 or higher and the sign-in or step-up that justifies it must be no more than 30 minutes old
MethodsPasskey; authenticator code (only on a session that began with a password); linked provider
A recovery code aloneNever enough to regenerate recovery codes or remove the authenticator: a live authenticator proof is needed
Rate limitStep-up attempts are rate limited per account

Operations that need a fresh second factor

OperationNeeds step-upNotes
Add, rename or remove a passkeyYesYou are emailed when a passkey is added or removed
Set, change or remove a passwordYesChanging the password also asks for the current password under its own limit
Enrol or remove an authenticator appYes
Regenerate recovery codesYesSupersedes the old batch
Change the primary email addressYesThe old address gets a 24-hour link that reverts the change and signs everything out
Request or download a personal-data exportYes
Deactivate the accountYesA typed confirmation phrase is also required
Create or revoke a personal access tokenYes
Mark a device as trustedYes
Link or unlink a federated accountYes
Register or edit an OAuth client of your ownYesAvailable only when an operator enables the feature
Revoke a single sessionNo: a live session is enoughAn attacker with a session could therefore end your other sessions; an operator flag can require step-up here
View sessions, activity and connected appsNo

Risk engine and sign-in alerts

At the end of every sign-in, after the factors have been checked, a risk engine assesses the attempt and decides to allow it, to demand a stronger factor, or to refuse it. An unfamiliar browser, place or pattern may be asked to confirm with a stronger factor. It is a rule-based score, not a machine-learning model, and it never uses advertising or behavioural profiling data.

Every outcome and the signals behind it are stored with the sign-in transaction and audited.

Sign-in alert behaviourDetail
TriggerA sign-in from a browser the account has not seen
What is sentAn email to your verified addresses with a browser label, country, network address and sign-in method
Record lifetimeThe alert record is deleted when it is used or after 7 days
The "Review this sign-in" link and its "No, sign that session out" and "No, secure my account" buttonsScanner-safe: opening the link changes nothing; the change happens on a confirmation POST. Every invalid, expired or used link gets the same page
Owner decision pendingAn account with no second factor may be admitted on an unrecognised browser and alerted, instead of refused; the permanent default is not decided

Note

The sign-in alert is an email about a sign-in you may or may not have made. Separate notices are also emailed for a detected token reuse and for a sign-in blocked by the risk engine (these notices are rate limited per account).

Rate limits and abuse controls

Sign-in, recovery, token and API endpoints are rate limited. The counters are kept in the database so concurrent attempts cannot slip past a shared counter, and a request that exceeds a limit is refused with HTTP 429 and a Retry-After header. Account email sends are capped. The exact limits are not published.

Additional controlDetail
LockoutNone. Repeated failure is throttled; there is no permanent lockout. The owner is emailed when repeated failed attempts hit the account
Response-time floorA minimum response time on the email-sending endpoints, hiding a faster path for existing accounts
Request body limit256 KiB for API requests
Header block limit8 KiB
Request timeout10 seconds
Outbound account emailCapped per account and per address. Account security notices (credential changes, session endings, sign-in alerts, token reuse, deactivation, export) are sent directly and do not use these caps, so an attacker cannot silence your alerts

Recovery paths

Account recovery is ordered by strength, and the flow offers only the strongest path available to you. Every recovery link is single use, valid 15 minutes, and bound to the browser that requested it (a link opened elsewhere requires a new request).

PathRequirementEffectStatus
R1Another enrolled passkeyOrdinary sign-in; no recovery neededBuilt
R2A recovery code and a link to a verified emailA limited session confined to the security page until a factor is re-enrolledBuilt
R3A link to a verified email onlyA 24-hour hold; every verified address is emailed a hold notice with a one-click cancel link, and a signed-in session sees a pending-recovery notice with a cancel button on the Security page; after the hold, the same limited sessionBuilt
R4Administrator-assisted recoveryAn administrator starts recovery for an account; every verified address of the target gets a hold notice with a one-click cancel, and the target then asks again from their own browser after the hold. The specification adds dual control for the internal organizationBuilt only for a single-administrator case on the internal organization, behind an operator flag that is off by default; dual control is not claimed
Recovery detailValue
Recovery link lifetime15 minutes
Hold for the R3 path24 hours
Window to re-open a held recovery7 days
Settled recovery rowsDeleted 14 days after completion or cancellation
Magic sign-in link lifetime15 minutes
Email verification link lifetime24 hours; an unverified address is invisible to sign-in and swept after 24 hours
Invitation lifetime7 days, single use

Cryptography and key management

PurposePrimitiveNotes
Token signing, audit anchors, security event tokens, logout tokensEd25519 (EdDSA); ES256 optional per clientEach signing key row carries a purpose and the issuers it may sign for; a validly signed token of the wrong type or issuer is rejected
Data export signingA key of purpose exportSignature and document hash are over the plaintext so a download verifies offline
Envelope encryption of recoverable secretsAES-256-GCM, random 96-bit nonce, additional data bound to the rowPer-purpose data keys wrapped by a key-encryption key
Password hashingArgon2id, 64 MiB, 3 passes, 4 lanesSee the password section
Opaque secret storage (sessions, codes, links, tokens)SHA-256 over 32 random bytesFixed 32-byte hash domain
Recovery codesHMAC-SHA-256 under a pepper held as a sealed secret
RandomnessOperating-system CSPRNG32 bytes for every secret
Secret comparisonConstant timeLength mismatch handled before the comparison
Webhook signaturesHMAC-SHA-256 over timestamp and bodyReceivers are told to reject skew above 5 minutes
TLSTerminated at the hosting edgeHSTS with a two-year max-age and includeSubDomains is sent by the application
Post-quantum signaturesNot offeredNo standard algorithm identifier exists yet; the key set stays crypto-agile

Key hierarchy

LayerWhat it isWhere it lives today
Key-encryption key (KEK)Wraps every data keyA master key held by the hosting platform's environment as a sensitive variable
Data keys (DEKs)One per purpose: authenticator secrets, client secrets, recovery pepper, webhook secrets, refresh successorsWrapped rows in the database
Signing keysPrivate key material for tokens, anchors, logout and security event tokensAES-256-GCM sealed rows in the database, opened under the master key
Export artifact keysA fresh random 256-bit key per export, wrapped by the key providerInside each stored artifact for at most 7 days
Public keysThe published key set (JWKS), cached up to 1 hourServed from the database

Secrets at rest

SecretAt-rest formReversible
PasswordArgon2id hashNo
Session secretSHA-256 digestNo
Refresh tokenSHA-256 digest; the successor token is sealed to support the retry graceSuccessor only
Authorization code, PAR reference, magic link, recovery link, invitation, verification linkSHA-256 digestNo
Personal access tokenSHA-256 digest; shown onceNo
Authenticator (TOTP) secretSealed with AES-256-GCMYes, to verify codes
Recovery codesHMAC-SHA-256 keyed hashNo
PasskeyPublic key onlyNot secret
Client secret (confidential clients)SealedYes
Webhook secretSealedYes, to sign deliveries
Federation client secretSealedYes
Queued outbound emailSealed while queued; finished rows are purgedYes, until sent
Personal-data exportSealed per artifact, then deleted after the download windowYes, for the owner's download
Network address of a sessionStored plain, then cleared by a retention job after 90 days; ended sessions are deleted after 90 daysSee the privacy page

A database test census fails the build when a column that looks like a plaintext secret appears, and a database check on every ciphertext column refuses a value too short to be a real ciphertext, so a plaintext string written into a ciphertext column fails at the database.

Transport and headers

Header or controlValueWhere
Content-Security-Policydefault-src 'none'; scripts only from this site with a per-response nonce and 'strict-dynamic'; styles only from this site; fonts, images (this site and data URIs) and connections limited to this site; frame-ancestors 'none'; base-uri 'none'; form-action 'self' on account and trust pagesAccount, sign-in and trust pages
Frame embeddingX-Frame-Options: DENY and frame-ancestors 'none'All pages
MIME sniffingX-Content-Type-Options: nosniffAll pages
ReferrerReferrer-Policy: no-referrerAll pages
Cross-origin openerCross-Origin-Opener-Policy: same-originAll pages
Browser featuresPermissions-Policy disables camera, microphone, geolocation, payment and USBAll pages
HSTSmax-age=63072000; includeSubDomains (two years), production builds onlyAll pages
CachingA response without a cache directive is no-store; only the key set, discovery documents, avatars and static assets are cacheable, and a cacheable response that sets a cookie is forced back to no-storeEverywhere
Inline scripts and stylesNo style attribute anywhere in the web app and no inline event handlers; a lint rule enforces itWeb app
Third-party resourcesNo third-party scripts, fonts, images or analyticsAll pages
Admin console headersA policy that forbids framing and locks the base URI, object sources and form action, no sniffing, no referrer, a restrictive Permissions-Policy, same-origin opener and resource policies, and two-year HSTSAdmin console; it has no script policy yet, which needs a per-request nonce
RedirectsOnly to a registered redirect URI; the post-login target in the account area is a closed list, never a supplied URLOAuth and account
Same-origin checkEvery state-changing account and sign-in request refuses a cross-origin request before doing anythingAccount API and /auth/v1
LogoutA cookie-only GET shows a confirmation page and ends nothing; a session ends at once only for an ID token hint naming the signed-in principal or a POST carrying a token a cross-site form cannot computeOAuth logout
Request hygieneJSON only, content type enforced, strict schemas that reject unknown keys, prototype-pollution keys strippedAPI

Audit log

Security-relevant events are written to an append-only log in which each row is chained to the previous row by a hash. The database refuses UPDATE and DELETE on it. Each event carries identifiers and hashes rather than names or email addresses; an address in a payload appears only as a hash plus its last two characters.

NamespaceWhat it records
authSign-in started, identify, password and passkey results, passkey counter regression, authenticator results, recovery code use, step-up, risk outcomes and blocks, session created and revoked, logout, magic links, recovery started, cancelled and completed, federated sign-in
credPassword set, changed or removed; passkey added, removed or renamed; authenticator enrolled or reset; recovery codes regenerated; email added, verified, made primary or removed; federated account linked or unlinked
dirPrincipal created, suspended, reactivated, deactivated and erased; organization and membership changes; invitations; service accounts, workloads and agents
oauthClient created, updated and keyed; codes and tokens issued, refreshed, exchanged and revoked; refresh reuse; consent granted and revoked; back-channel logout
authzAuthorization decisions that are not low-risk allows; role and binding changes; policy lifecycle; elevations; recertification; impersonation start and end; containment denials
keySigning key generated, promoted, retiring and retired; emergency rotation; data and key-encryption key rotation
platformEvery use of the platform role that bypasses row-level security; break-glass activation and deactivation; configuration changes; migrations
auditAnchors published; verification results; exports requested and completed
webhookWebhook created, updated, deleted; final delivery failures
PropertyValue
ChainEach row's hash is the SHA-256 of the previous hash and the canonical form of the row
AnchorsThe chain head can be signed with an anchor key and published; a verification command checks each published anchor against a public key obtained outside the database
Scheduled anchoringA worker exists, but it is off by default and no publication target is configured in the committed environment files, so anchoring is currently an operator command
Offline verificationAn operator command verifies anchors; a standing scheduled verification is not built
Retention default2,920 days (8 years)
Retention boundsThe validated range is 365 to 3,650 days, but the runtime reads one deployment-wide setting, not a per-organization one; the internal organization's floor is 2,920 days
PruningA compaction job exists but is off unless an operator enables it, and it only acts below the newest signed anchor with a database floor of 365 days. Today audit records are retained without automatic pruning
Your own eventsVisible on the Activity page for the last 90 days

Administrative and break-glass controls

ControlWhat is built
Administrative API accessOnly tokens issued to the administrative console client, or a named automation client with a proof of possession, are accepted; anything else is refused. While the console client is unset, every administrative request is refused
Admin consoleA separate deployment that signs in through the same OpenID Connect flow with a private key assertion and keeps its session in an encrypted cookie
High-risk administrative writesNeed an active elevation or a fresh second factor of the caller's own; an elevation's requester, beneficiary and approver must be distinct
Platform roleThe role that can bypass row-level security is fenced: every write must be preceded by a platform-access audit row in the same transaction
Break-glassA separate console-only session, refused by the account API. Online activation needs hardware-key assertions from two distinct live platform administrators; the weaker approval-token mode is refused. While the platform has exactly one platform administrator, an offline operator command with direct database access and the master key can activate it with the second administrator waived
Support impersonationThe target is emailed when a support access window starts and when it ends
First platform administratorProvisioned by the owner with an operator command. An optional allowlist, off by default and not set in the committed configuration, can grant the first administrator at sign-in to a verified address when no administrator exists yet
DelegationAn actor can only grant what it may itself delegate; wildcards never cover authorization-management permissions; an agent's authority is a subset of its delegator's
Operator command-line toolReads the key-encryption key from the environment only; credential files are created with owner-only permissions

Note

Operators of this platform are a very small group. The break-glass design assumes their devices are safe. A break-glass activation raises an email alert to the operator address, but only when the daily monitor job runs, so it can arrive up to about a day later.

Supply chain and dependency policy

RuleHow it is enforced
Locked dependenciesA committed lockfile; CI installs with a frozen lockfile and fails if it disagrees with the manifests
Fresh releases are held backA 7-day minimum release age, asserted against the effective configuration
No install scripts by defaultOnly a reviewed allowlist of packages may run build scripts; a bypass in any configuration layer fails the gate before install
No resolution rewritingOverrides, patches, tarball or git sources and lockfile entries without an integrity hash fail the gate
Pinned package managerAn exact pnpm version asserted in CI and in the hosting install command
Advisory scanningAn advisory scan on every change to dependencies and weekly, with a tracked allowlist
Dependency reviewAdding, removing or changing a version in the cryptography, OIDC, authentication or authorization packages fails until the reviewed table is edited in the same commit
One version per packageA single specifier per external package across the workspace
Small cryptographic closureThe cryptography package's full production dependency closure is capped (currently one package)
Internal packages never reach a registryInternal references must use the workspace protocol
No database-push toolA command that applies schema changes blindly is banned from every manifest and the lockfile, after an outage it caused in the earlier platform; schema changes are numbered, checksummed SQL migrations
DeploymentsOnly the main branch deploys; preview deployments are off and secrets are scoped to Production only
Automated updatesWeekly, grouped, with a cooldown matching the release-age rule

Logging and observability

PropertyDetail
Request logOne JSON line per request on standard output, filtered through a closed list of fields
RedactionA key outside the list, or a value outside its declared domain, becomes [redacted]
Metrics and tracesExported over OTLP only when an endpoint is configured
Operational alertingA daily monitor can email an operator address; it needs a daily scheduled job to run
Logged fieldMeaning
TimestampEpoch milliseconds from the server clock
Request identifierA random identifier minted by the server
Trace identifierA W3C trace id
RouteThe matched route pattern, never the requested path or query
Method and statusHTTP method and response status
DurationWall-clock milliseconds
Organization identifierAn identifier, not a person
Client identifierAn OAuth client id from a verified authentication
CallerA truncated keyed hash (HMAC) of the caller's address bucket; it correlates one caller without revealing an address
Error classThe class name only; the message never leaves
OutcomeOne of ok, denied, rate limited, client error, server error
Deployment identifierFrom the environment
Never loggedWhy
Passwords, password hashes and breach-check prefixesCredentials
Cookie values, bearer tokens, refresh tokens, codes and linksCredentials
Raw email addressesPersonal data; audit payloads use a hash and a two-character mask
Raw network addresses in the request logReplaced by the keyed hash above
Error messagesThey can act as an oracle
Request paths and query stringsThey can carry secrets

A support tool strips credentials, tokens, cookies, private key material, password hashes and email addresses from any HAR file or log bundle before it is stored, and refuses images and archives rather than pretend to scan them.

Backups and disaster recovery

ItemAs documented
Primary databaseA managed Postgres service
Provider point-in-time windowNot measured for this deployment: an earlier project measured six hours, and the figure for the current project is unverified
Nightly encrypted dumpA workflow is written that dumps with a dedicated login, encrypts to a public key and uploads to object storage; it has not been run yet
Owner copyA command and a timer exist for the owner's own copy; the timer is documented, not installed
Monthly restore drillA workflow is written that restores the newest dump into a scratch database and runs the schema check; it has not been run yet
Missed-backup alertThe backup workflow is written to ping an external healthcheck service, and has not yet run. The monitor's own stale-backup check is off in the committed staging configuration, which is the one production runs
Key-encryption keyNever stored with a dump
Recovery point objectiveNot claimed; inside the provider's window it is seconds in principle, outside it up to a day once the owner's timer is installed
Recovery time objectiveNot measured on real data; no figure is claimed
Data locationThe primary database is in AWS US East 1 (N. Virginia), not in India; the placement the specification calls for was waived by the owner for this period

Testing and assurance

ActivityState
Unit and integration testsRun in CI on every push against a real Postgres cluster started for the tests; no containers
Security regression registerA mapped list of threat tests; the build fails if a listed test is missing or skipped
Named invariantsRules such as single-audience tokens, sender-constraint, refresh reuse revocation and secrets-at-rest each have a test
Browser end-to-end testsA real-browser suite drives sign-in, registration, recovery, step-up, sessions, tokens, privacy actions and consent
Accessibility checksAn automated axe scan at WCAG 2.2 AA tags in light and dark preference at two widths; manual screen-reader and zoom checks are still owed
FuzzingSpecified for parsers; do not read this as a claim that fuzzing runs
Internal reviewThe repository records internal security audits and a re-architecture review; findings and what was deferred are written down
External penetration testNone
CertificationsNone
OpenID conformance certificationNot obtained

Incident handling

Runbooks exist for signing key compromise, a client key compromise, a database read breach, an administrator account compromise, an audit chain mismatch, exposed support material and a locked-out sole administrator, plus a notification runbook. Each step in them is tagged as built or not built, so none describes a future state in the present tense. The specification names reporting clocks (6 hours to the national CERT and 72 hours to the Data Protection Board for the classes it lists); the notification runbook carries a template for both. The vulnerability disclosure page says how to report a problem to us, and the incident response page describes how an incident is handled.

What you can do to stay safe

ActionWhy it helps
Add a passkeyIt is phishing resistant and satisfies a second factor on its own
Use a password manager and a long unique passwordThe platform rejects the weakest passwords but cannot know reuse
Enrol an authenticator app and keep your recovery codes offlineRecovery codes are shown once
Open the Sessions page after travel or a new phoneRevoke sessions and devices you do not recognise
Read sign-in alert emailsUse "No, sign that session out" if you did not sign in
Keep the mailbox that receives recovery links secureAnyone who controls it can start recovery by design; the 24-hour hold and your notices are the safeguard
Prefer short-lived personal access tokens with an IP allowlistThe ceiling is 90 days; shorter is safer
Review Connected appsRevoke any application you no longer use
Report anything odd to security@tdacorp.inSee the vulnerability disclosure page

What this page does not claim

  • TDACorp holds no security certification. There is no SOC 2 report, no ISO 27001 certificate and no third-party audit.
  • No outside party has verified these statements. They are written by the team that maintains the code, from the code.
  • This page describes the identity platform only, in pre-production. A product built on this platform enforces its own authorization in its own database.
  • It is not a threat model, a penetration test report or a claim that the platform is unbreakable.
  • Production hardware or cloud key management is not in place. Pre-production runs a software key provider, and the weaker custody described in the cryptography section is real.
  • No uptime or recovery objective is promised. Service level objectives exist as design targets and only some of the metrics behind them are emitted.
  • Background work (exports, erasure, key rotation, webhook delivery, expiry sweeps) is driven by a daily scheduled job and by ordinary traffic on the current hosting plan, so it can run up to about a day late.
  • There is no permanent account lockout; failed attempts are throttled, and the owner is emailed when repeated failed attempts hit the account.
  • A break-glass activation is alerted to the operator only by the daily monitor job, so the alert can be up to about a day late.
  • The breach check is best effort: it depends on an external service.
  • The resolution of the client address used by per-address limits has not been verified against the live edge.
  • Scheduled audit anchoring and automatic audit pruning are off in the committed configuration, and offline standing verification is not built.
  • Backup jobs are written but have not been run, and no restore time has been measured.
  • Federated sign-in code exists but no provider is registered, so none is offered today.
  • Revoking a single session does not need step-up unless an operator enables the flag.
  • Imported older password hashes are not verified; those accounts must set a new password.
  • The cost of the decision API's event stream on the hosting plan has not been measured.
  • The platform has not been through the OpenID Foundation conformance suite.
  • Audit retention is not a setting an organization can change; the page quotes defaults for session lifetimes and the step-up window.

The disclosure policy exists because we expect to be wrong somewhere. If you have found where, tell us at security@tdacorp.in.