For version 1.5.0 on AWS Marketplace, by HabileLabs. Questions: support@habilelabs.io.
Aadhaar Masking finds Aadhaar numbers and Aadhaar QR codes in KYC documents and covers them. It masks card scans, phone photos, e-Aadhaar printouts, multi-page loan PDFs, ZIP archives and Excel sheets. It runs entirely inside your own AWS account: one CloudFormation stack, your VPC, your S3 bucket. No document is sent to HabileLabs or anyone else; by default the stack sends HabileLabs only a daily page count (section 5), which you can switch off.
1. Before you start
- Subscribe to Aadhaar Masking on AWS Marketplace, with the account you will deploy in. Choose the page packs and the term (12, 24 or 36 months).
- Region: deploy in Asia Pacific (Mumbai), ap-south-1. The template also runs in us-east-1, us-east-2, us-west-2, eu-west-1, eu-west-2, ap-southeast-1 and ap-southeast-2.
- A VPC with two or more subnets in different Availability Zones. The default VPC works as it is. Private subnets need a NAT gateway, or VPC endpoints for ECR, S3, Lambda, DynamoDB, Batch, Secrets Manager, CloudWatch Logs and License Manager.
- Lambda concurrency quota of at least 140. The stack reserves 40 parallel masking environments, and AWS keeps 100 unreserved. Check it in Service Quotas > AWS Lambda > Concurrent executions. Most accounts have 1,000; a new account may have less, so ask for an increase or deploy with a lower Parallel masking environments setting.
- Permission to create IAM roles: the stack creates its own, scoped to its own resources.
2. Deploy in one click
- Open Launch stack from the product's usage instructions on AWS Marketplace, or this link: Launch stack in Mumbai.
- Check that the console shows Asia Pacific (Mumbai) at the top right.
- Under Network (required), pick your VPC and two or more of its subnets in different Availability Zones.
- Under Alerts (recommended), enter an e-mail address for alarms. You will get one e-mail from AWS SNS to confirm the subscription.
- Leave everything else as it is. Tick I acknowledge that AWS CloudFormation might create IAM resources and choose Create stack.
- Wait for CREATE_COMPLETE, about 8 to 10 minutes. The Outputs tab then shows what you need:
| Output | What it is |
|---|---|
ApiEndpoint |
The API's base URL |
ApiKeysSecretArn |
The Secrets Manager secret that holds your API keys |
LoadBalancerDnsName |
Point your own DNS name here (CNAME) if you use one |
AlarmTopicArn |
The SNS topic every alarm goes to |
OutputBucketName |
The S3 bucket for uploads and masked copies |
MaskingFunctionName |
The masking function, for logs and metrics |
What the stack creates:
- An API on Amazon ECS (Fargate), behind an Application Load Balancer with AWS WAF.
- The masking engine on AWS Lambda, with up to 40 environments working in parallel. A long PDF is split into chunks that are masked at the same time.
- AWS Batch on Fargate for ZIP archives and very long documents.
- An S3 bucket for uploads and masked copies, a DynamoDB table for task state and page counts, a Secrets Manager secret for API keys, and CloudWatch alarms with an SNS topic.
3. Settings
Only the network is required. Everything else has a working default.
| Setting | Default | Change it when |
|---|---|---|
| VPC, Subnets | none (required) | Always: pick a VPC, then two or more of its subnets (the list shows every VPC's subnets; match the VPC ID) |
| Public IP for tasks | ENABLED | Your subnets are private with a NAT gateway or VPC endpoints: DISABLED |
| Alarm e-mail | none | Recommended: who should hear about alarms |
| Endpoint type | internal | Callers outside your network must reach it: internet-facing (needs the next two) |
| ACM certificate ARN | none | Internet-facing: a certificate in this Region for your hostname |
| Your API URL | none | With a certificate: https:// plus your hostname, used in the links the API returns |
| Allowed callers (CIDR) | 0.0.0.0/0 | Internet-facing: your callers' ranges. Internal: optional narrowing |
| Parallel masking environments | 40 | More simultaneous traffic: raise. Small quota: lower (10 still works, long PDFs take longer) |
| Always-warm environments | 0 | The first request after idle must be fast: 1 or more (about USD 95 a month each) |
| Send HabileLabs a daily page count (ShareUsage) | true | You do not want the daily count sent: false (section 5) |
| Images | empty: this version's images | Never; leave both empty, so every update brings that version's images |
4. Your first request
Get your API key. In Secrets Manager, open the secret named in ApiKeysSecretArn and choose
Retrieve secret value; the key is under initial. From a shell:
aws secretsmanager get-secret-value --secret-id <ApiKeysSecretArn> --query SecretString --output text
Call the API from inside your network. With the default internal endpoint, call it from a server in the VPC (EC2, ECS, Lambda in the VPC), from a connected network, or from AWS CloudShell in a VPC environment.
curl -X POST "<ApiEndpoint>/aadhaar-masking" -H "aadhaar-masking-key: <your key>" -F "file=@kyc-card.jpg"
An image or a short PDF is answered at once (200) with a link to the masked file:
{ "status": 200, "message": "File processed successfully.",
"downloadUrl": "https://...", "maskedPages": 1, "maskingStatus": true }
Download the masked file promptly: the link expires after 15 minutes, and the bucket deletes uploads and masked copies after a day.
Long PDFs and ZIP archives are masked in the background. The answer is 202 with a taskId
and a statusUrl; poll the status URL with the same key until status is COMPLETED, then use
its downloadUrl. A 500-page PDF takes about a minute. A ZIP comes back as one masked archive with
a per-file report.
More:
<ApiEndpoint>/docs: the full API guide (modes, Excel, files in your own S3, every field and error).<ApiEndpoint>/ui: a browser page to try files by hand.
Files: JPG, PNG, TIFF and PDF up to 100 MB, ZIP up to 500 MB, and Excel (.xls, .xlsx) through
POST /aadhaar-masking-excel. Password-protected PDFs are refused, not masked.
What is masked:
- The 12-digit Aadhaar number: the first eight digits are covered and the last four stay visible. All twelve are covered when the number's reading direction cannot be established.
- The Aadhaar QR code, fully.
- Names, photos, dates of birth and addresses are left as they are.
5. Your contract and page usage
curl "<ApiEndpoint>/usage" -H "aadhaar-masking-key: <your key>"
{ "subscription": "active", "packs": {"starter": 1}, "pagesAllowance": 250000,
"pagesUsed": 1234, "contractYearStart": "...", "contractYearEnd": "...", "countedAt": "..." }
- Every masking answer carries the same figures in headers:
X-Pages-Allowance,X-Pages-UsedandX-Contract-Packs. - What counts: every image, PDF page and page inside a ZIP is one page; Excel workbooks are not counted. The count is refreshed every five minutes.
- Masking never stops when you pass your pages. The alarm topic is told at 80 % and 100 %. To get more pages, add a pack to your contract on AWS Marketplace (Your Marketplace software > Manage subscriptions).
- Without a live contract the API answers masking requests with
403: "No active AWS Marketplace subscription for this product". The no-subscription alarm also goes off. After you subscribe or renew, the API notices within about a minute.
Usage reporting (ShareUsage)
From 1.4.0, while ShareUsage is true (the default), the API sends HabileLabs one small report
a day, early in the morning (UTC). It contains:
- the pages masked on the previous day and the 7 days before it, and this contract year's total;
- your packs and contract dates, as your AWS Marketplace licence states them;
- which deployment counted them: AWS Region, stack name and ID, version.
It contains no documents, no Aadhaar numbers, no file names and nothing a document contains. The request is signed with the API task's own AWS credentials, so HabileLabs knows which AWS account sent it without any key to manage. HabileLabs uses it to see your installs and usage in the HabileLabs Marketplace portal, to tell you before you run out of pages or your contract ends, and to support you.
- Switch it off: update the stack with Send HabileLabs a daily page count set to
false. The setting and its IAM permission are removed and nothing is sent; masking works exactly the same. - Network: the report goes to
https://api.marketplace.habilelabs.ioover the internet. Tasks in private subnets without a NAT gateway cannot send it; nothing else is affected. - In the logs:
usage_report_sent(orusage_report_retry/usage_report_refused) in the API task's log group, once a day.
6. Making the API reachable
- Internal (default): reachable only from the VPC and the networks connected to it (peered
VPCs, Transit Gateway, VPN, Direct Connect). Callers use
ApiEndpointover HTTP inside your network, with the API key. - Internet-facing:
1. Request or import an ACM certificate in this Region for a hostname such as
masking.example.in. 2. Update the stack: set Endpoint type to internet-facing, set the certificate ARN and Your API URL (https://masking.example.in), and narrow Allowed callers to your callers' ranges. 3. Point a CNAME for the hostname atLoadBalancerDnsName.
The API is then served over HTTPS only (TLS 1.2 and 1.3).
- AWS WAF in front of the API limits each address to 300 requests in five minutes, blocks
addresses on AWS's IP reputation list, and refuses large bodies outside the upload routes. Every
request also needs a valid API key.
- API keys: to add or rotate one, edit the secret. It is a JSON object of name to key, for
example {"initial": "...", "loans-app": "..."}. Changes apply without a redeploy.
7. Security and data handling
- Documents never leave your account. The only outside calls are the licence check with AWS License Manager and, unless you switch it off, the daily page count to HabileLabs (section 5).
- Storage: uploads and masked copies live in the stack's S3 bucket (
uploads/,masked-output/) and are deleted automatically within one to two days. The bucket blocks public access, encrypts at rest (SSE-S3) and refuses non-TLS access. - Records: DynamoDB holds task state and daily page counts, no document content. CloudWatch logs hold no document content and no keys.
- Masked PDFs are rebuilt from images, so a covered number cannot be recovered from them.
- IAM: each component has its own role, limited to the stack's own bucket, table, secret, queue and function.
8. Alarms
Every alarm publishes to the AlarmTopicArn topic. Subscribe your e-mail, pager or chat hook to it.
| Alarm | Meaning |
|---|---|
| no-subscription | No live AWS Marketplace contract; masking is refused |
| page-allowance | This contract year reached 80 % or 100 % of its pages |
| lambda-throttles | All parallel environments busy; requests wait or go to the background. Consider raising Parallel masking environments |
| lambda-errors, lambda-duration-p95 | The masking function failed or ran close to its limit |
| alb-5xx-rate, api-no-healthy-task | The API is failing or down |
| documents-rejected | A document was refused because a page could not be masked |
9. Costs
- The software: your AWS Marketplace contract, the page packs and term you chose.
- AWS resources: you pay AWS directly for what the stack runs in your account, typically about USD 50 to 180 a month plus about USD 0.0003 a masked page.
- What the monthly figure covers: the load balancer, one API task, AWS WAF, a warm-up ping and any always-warm environments. The per-page figure is Lambda, S3 and DynamoDB.
10. Updating to a new version
When a new version is announced:
- Open CloudFormation > your stack > Update > Replace current template, with the new version's template URL (in its usage instructions).
- Keep your settings, but check that the two image fields are empty: empty means "this version's images". A stack created from 1.3.0 or earlier has addresses filled in there; clear them, or the stack keeps its old images.
- Check Send HabileLabs a daily page count: an update to 1.4.0 from an earlier version starts
with its default,
true. - Choose Update stack.
The stack copies the new engine image and switches over without downtime.
11. Removing it
Delete the stack in CloudFormation. Its two S3 buckets (masked files, load-balancer logs) are kept on
purpose, so that no file is lost by accident. The files bucket empties itself within a day or two;
delete both buckets once they are empty. A log group named
/aws/lambda/<stack>-ImageCopyFunction-… can reappear after the deletion (the copy function's last lines
arrive after its log group is removed); delete it too. Your AWS Marketplace contract is separate: cancel or let
it end in Manage subscriptions.
12. Troubleshooting
| What you see | What to do |
|---|---|
| Create stack refused: "Pick subnets from the VPC you selected" | The subnet list shows every VPC's subnets: choose only those whose VPC ID matches the VPC field |
| Stack fails: "ReservedConcurrentExecutions ... below its minimum value of [100]" | The account's Lambda quota is under 140: lower Parallel masking environments, or ask AWS for a higher quota |
403 {"error": "Forbidden"} |
The API key is missing or wrong: header aadhaar-masking-key |
403 "No active AWS Marketplace subscription for this product" |
Subscribe, or renew, with this account; the API notices within a minute |
| Connection times out | Internal endpoint: call from inside the VPC or a connected network, and check Allowed callers |
202 instead of 200 |
Normal for long PDFs, ZIPs and busy moments: poll the statusUrl |
422 |
Unsupported file type, file too large, or a password-protected PDF |
403 with a plain page instead of JSON |
AWS WAF blocked it: more than 300 requests in five minutes from one address, or an address on AWS's reputation list |
| lambda-throttles alarm | Raise Parallel masking environments (and your Lambda quota if needed) |
usage_report_refused with 403 in the API log |
The stack runs in an AWS account other than the subscribing one; tell support which account, or set ShareUsage to false |
usage_report_retry every day |
The API tasks cannot reach the internet (private subnets without NAT); nothing else is affected |
13. Support
E-mail support@habilelabs.io with your AWS account ID, the stack name and, for a specific
request, the x-request-id header from its answer. We answer within one business day (IST, Monday
to Friday).
Masking is automatic. Handwritten numbers and Virtual IDs (VID) are not masked. For high-stakes uses, spot-check results.