Version 1.0 · Effective 29/08/2026 · The Vietnamese text is the binding version; this English text is a reference translation
This document describes the security measures that are actually in place. Where something is not in place, it says so. Following ordinary information-security practice, it does not give specific detection thresholds, administrative paths, or configuration detail that could be used to attack the system.
| Measure | Status |
|---|---|
| HTTPS/TLS across the entire public surface | Yes |
| HTTPS enforced (HSTS), one year, including subdomains | Yes — on the API surface, the account pages and the operator console |
| Framing by other sites blocked | Yes — all three surfaces |
| Content-type sniffing blocked (nosniff) | Yes — all three surfaces |
| Referrer information controlled | Yes |
| Camera / microphone / geolocation / payment permissions restricted | Yes |
| Technology-identifying headers hidden | Yes |
| Content Security Policy (CSP) | NOT IN PLACE — recorded as work to do |
Configuration at startup refuses to run in production if the application address is not HTTPS.
| Item | How |
|---|---|
| Algorithm | scrypt, 64-byte derived key |
| Salt | 16 random bytes, unique per password |
| Stored format | self-describing about its algorithm, so it can be upgraded later |
| Comparison | constant time |
| Strength requirement | at least 8 characters, with at least one letter and one digit |
| Sign-in attempt limits | Yes — by IP address and by email address, independently |
| Account lockout after repeated wrong passwords | NOT IN PLACE — rate limiting only |
There is no legacy hash format and no fallback path anywhere in the system — every password is scrypt.
There is no two-factor authentication for customers yet. That is a conscious decision at this stage and is recorded as work that should be done. Two-factor currently applies to operators only.
| Item | How |
|---|---|
| Generation | 28 cryptographically random bytes — 224 bits of entropy |
| Storage | only an HMAC hash, keyed with a server-side secret |
| Plaintext | returned exactly once at creation; there is no path to read it back |
| Revocation | immediate, and cannot be undone |
| Automatic revocation | when an organisation is suspended, when a machine is disconnected, and when the command-line tool rotates its key |
| Per-key spend cap | Yes — you set a monthly spend cap on each key yourself |
What this means for you: if you lose a key, TexAPI cannot send it back to you — only revoke it and issue a new one. If TexAPI’s database were read without authorisation, your keys could not be reconstructed from it.
Two limitations that have to be disclosed:
Authorization header.| Kind | How it is protected |
|---|---|
| Infrastructure supplier access keys | AES-256-GCM at rest with integrity authentication; decrypted per read; any temporary cache holds ciphertext only |
| Operators' two-factor secrets | AES-256-GCM at rest |
| Session tokens, email verification tokens, password reset tokens, device codes | only an HMAC hash is stored |
| Social sign-in provider tokens | not stored — used within the request and discarded |
Your API key is never forwarded to the infrastructure supplier. An authentication-header block list guarantees that; TexAPI uses its own key when calling the infrastructure.
One limitation to record: the key used to encrypt the secrets above is derived with a single simple hash and has no key version — meaning there is currently no path to rotate the encryption key. Recorded as work that should be done.
| Item | Customer | Operator |
|---|---|---|
| Lifetime | 30 days | 8 hours |
| Stored on the server | hash only | hash only |
| The two session types cannot be used interchangeably | Yes — separated at the database query layer, not in checking code | Yes |
| You can review and revoke them yourself | Yes — with IP, device and last activity | Yes |
| All revoked on a password change | Yes | — |
| All deleted on a password reset | Yes | — |
| Token rotation during a session | NOT IN PLACE | NOT IN PLACE |
| Expiry on inactivity | NOT IN PLACE — a fixed lifetime only | — |
The last two rows mean this: a token stolen on day one still works on day 30 unless you revoke it yourself. Which is why Settings → Security is a real tool, and worth using when something looks wrong.
The operator console is a separate application on its own domain, with its own cookie, an 8-hour session, and no search-engine indexing.
Role-based permissions, 6 roles and 50 distinct permissions. How it is enforced: the permission is the first mandatory argument of every administrative route, so a route that forgets to check one cannot be written. Every data-changing action goes through one single point, where an action does not compile unless it declares its permission, its audit action and its subject type.
Roles are designed on least privilege: the technical role has no financial permission at all; the finance role has no infrastructure permission and cannot read supplier secrets; the marketing role cannot see customers’ personal data; the support role can suspend and revoke but cannot issue refunds.
A role is looked up from the database on every request rather than baked into the session, so revoking a permission takes effect on the very next click — and the session of someone whose permissions were revoked is destroyed.
Money operations have two further barriers: a per-role amount ceiling, and a mandatory written reason on 13 kinds of sensitive permission. Two-factor authentication is mandatory for money operations above a threshold — an operator who has not enabled it is refused outright — but it is not yet mandatory for every operator, and is currently opt-in. There is no IP allow-list on the operator console.
Every data-changing action by an operator is recorded, in the same database transaction as the change itself. Consequently: there cannot be a recorded change with no trail, nor a trail for a change that was rolled back. Failed actions and actions refused for lack of permission are recorded too.
What each entry holds: who did it, the action, the subject, the reason, the IP address, the browser information, a snapshot of the state before and after, a correlation id, and the outcome.
Append-only, and the database enforces this rather than a convention: a mechanism at the database layer blocks every update and delete on the log table — correcting a wrong entry has to be done by writing a compensating one. The actor’s email is stored denormalised deliberately, so that an operator whose account is deleted cannot delete their own history with it.
An honest limit: this blocks ordinary and accidental modification. It does not stop somebody with full database privileges.
Masking is done at the service layer, not the interface layer — meaning masked data is not present in the response at all, rather than merely hidden when displayed.
| Kind | How it is masked |
|---|---|
| first character, last character and domain kept | |
| IP address | IPv4 kept to the third group; IPv6 to the first two groups |
| Browser information | reduced to the product name |
| Moderation excerpt | length only, with no content |
Unmasking requires a distinct permission that only 4 of the 6 roles hold.
Four identified masking gaps, being addressed — disclosed because half-masking is worse than none, since it creates a false sense of safety: the audit-log read surface returns unmasked state snapshots; the console overview embeds the most recent log entries without checking the personal-data read permission; display names are not masked anywhere; and the moderation excerpt is also written to the operational alerting surface, where there is no masking layer at all.
There are rate limits on: sign-in (by IP and by email), signup, forgotten password, password change, resending a verification email, the device-connection flow, redeeming a coupon, submitting a support ticket, every administrative route, and on API traffic itself.
The counters live in a shared cache with atomic operations. When the cache is unavailable, the system falls back to in-process counters rather than stopping enforcement — degrading to local, not opening the gate.
The per-plan numbers are in the API Terms. Abuse-detection thresholds are not published. Content moderation runs entirely in-process and does not send your content anywhere for analysis.
Two gaps, recorded honestly: there is no IP blocking, no blocklist and no country-level restriction — no table, no code and no configuration for any of it. And the route that consumes a password-reset token has no rate limit; the token is 256 bits so guessing is not a practical risk, but it is an ungated door.
| Measure | Status |
|---|---|
| TLS to the database, with certificate verification | Supported, configurable |
| Data isolation between customers | Yes — forced in the query rather than checked after reading; so a guessed identifier belonging to another organisation returns “not found” rather than confirming that it exists |
| Internal endpoints | require their own secret, compared in constant time, and refuse to open in production when the secret is unset |
| A copy of production data in the working tree | DELETED — 29/08/2026, no copy remains anywhere in the source tree |
| Automated backups | NOT IN PLACE on the new infrastructure — recorded |
| Restorability verified | NOT DONE |
| Backup encryption | NOT IN PLACE |
| Whether data content is stored in Vietnam | UNDETERMINED — the infrastructure is not settled |
The three backup rows are a serious shortcoming and TexAPI is not hiding them. What that means for you: keep your own copy of anything important — do not treat TexAPI as the only place holding something you cannot afford to lose.
| Kind of incident | What TexAPI does | Within |
|---|---|---|
| Suspected personal-data breach | investigate, contain, assess the scope | started within 24 hours of becoming aware |
| Confirmed personal-data breach | notify the competent authority | within the deadline the law sets |
| Confirmed personal-data breach | notify the people affected, saying which data, what has been done, and what you should do | no more than 72 hours from confirmation |
| Leaked credentials | revoke immediately, force a reset | immediately |
| An incident affecting the service | publish status | per Service Levels, section 2.3 |
TexAPI commits to not concealing an incident that affects you, including where the cause was TexAPI’s own negligence.
TexAPI welcomes vulnerability reports and will not take legal action against a researcher who follows the principles below.
Send to: support@texapi.dev with a subject beginning [SECURITY].
TexAPI’s commitments: acknowledge receipt within 3 working days; give an initial assessment within 10 working days; tell you when it is fixed; and credit you publicly if you would like that.
Permitted: testing against your own account; reading your own data; reporting logic flaws, authorisation flaws, authentication flaws and billing flaws.
Not permitted: accessing anyone else’s data — if you access it accidentally, stop at once, report it, and do not keep it; denial-of-service or load testing; automated testing that affects the service; social engineering aimed at TexAPI staff; physical testing; or disclosing a vulnerability before TexAPI has fixed it or before 90 days have passed.
TexAPI currently has no monetary bounty programme.
Security is shared. TexAPI cannot protect you from the following:
This list is public because publishing a gap with a deadline is more trustworthy than staying silent about it.
| Item | Priority |
|---|---|
| Fix the 4 masking gaps in the operator console | Before going on sale |
| Automated, encrypted backups with verified restorability | Before going on sale |
| Set retention periods on the tables still missing one | Before going on sale |
| Add a Content Security Policy (CSP) | Should do |
| Make two-factor mandatory for every operator | Should do |
| Two-factor authentication for customers | Should do |
| Store the last 4 characters of an API key so a leaked key can be identified | Should do |
| Session token rotation and expiry on inactivity | Should do |
| A rotation path for the secret-encryption key | Should do |
| An IP allow-list for the operator console | Worth having |
Progress is reflected in this document as items are completed.