CLI commands
rndl validate
Validate a domain's apple-app-site-association and assetlinks.json against the checks Apple and Google enforce, cross-check them against your route table, and gate CI on the result.
rndl validate fetches a domain's universal-link (AASA) and Android App Links files and validates
them the way the platforms do, not the way the docs suggest. Errors exit 1, so the same command
stops a broken universal link from shipping when you run it in CI.
rndl validate --domain example.com # AASA + assetlinks.json
rndl validate --domain example.com --json # full result as JSON
rndl validate --domain example.com --sarif # SARIF 2.1.0 for CI upload
rndl validate --domain example.com --package com.example.app --sha256 AA:BB:...What is checked
For https://<domain>/.well-known/apple-app-site-association:
- Reachable over plain HTTPS with a
200, and no redirects (Apple fetches with redirects disabled, so a301means Apple sees no file at all). - Under Apple's 128KB size cap.
- Valid JSON schema:
applinks.details[]withappIDs(or the legacyappID) in<TeamID>.<BundleID>form, andcomponents[]entries using/,?,#,exclude, andcomment. - Content-Type is a warning, not an error (Apple ignores it, but
text/htmlusually means a fallback page is being served).
For https://<domain>/.well-known/assetlinks.json:
- The relation is exactly
delegate_permission/common.handle_all_urlson anandroid_apptarget. package_namematches your app (from--packageor yourapp.json).sha256_cert_fingerprintsare well-formed;--sha256additionally asserts a specific fingerprint is present.
The route cross-check
Run validate from your app's directory (or pass --app-dir/--config) and it compares the AASA
components against your actual route table:
- A route that no non-excluded component covers is an error:
AASA_MISSING_ROUTE. That link will open the browser, not your app. - A component that matches no route is a warning:
AASA_ORPHAN_PATTERN. It is probably stale, or hiding a typo.
Skip the cross-check with --no-cross-check if you only want the file-level validation.
Flags
| Flag | Description |
|---|---|
--domain <domain> | Required. The domain to validate. |
--json | Print the full result as JSON. Mutually exclusive with --sarif. |
--sarif | Print a SARIF 2.1.0 report for code-scanning upload. Mutually exclusive with --json. |
--package <name> | Expected Android package name. Falls back to app.json when omitted. |
--sha256 <fingerprint> | Expected SHA-256 signing-cert fingerprint to look for in assetlinks.json. |
--app-dir <dir> | Expo Router app directory for the route cross-check. |
--config <module[#export]> | React Navigation linking module for the route cross-check. |
--no-cross-check | Skip matching the route table against AASA components. |
Caching and propagation
Apple serves AASA through a CDN, so changes can take 24 hours or more to reach devices (a
first-time fetch can take days). While
testing, add ?mode=developer to the Associated Domains entitlement to bypass the CDN, and
reinstall the app to force a re-fetch. Android re-verifies App Links on install and update.
rndl validate surfaces the caching note in its output and never treats a fresh change as a hard
failure.
Every diagnostic has a stable code (AASA_REDIRECTED, ASSETLINKS_FINGERPRINT_MISMATCH, and so
on) with a concrete fix. The
troubleshooting page maps the ten most
common deep-link failures to the code that catches each one.