Wardix documentation

Getting started

Wardix scans the artifact your CI already produces — the final .apk or .ipa — and diffs every scan against your previous build of the same app. You get a delta, not a wall of findings: what's new, what you fixed, and what's persisting. Findings come only from deterministic analyzers; an AI layer then explains each one in plain language and writes a concrete fix — it never invents a finding and never changes a severity.

Three steps

  1. Request a company account. At https://app.wardix.io/register, enter your name, company, email, optional phone and a password. Confirm the email from the message we send. Wardix then reviews the request and contacts you; registration alone does not activate the account. After staff records an offer for a Free or Trial Service or paid-for Subscribed Services and approves the application, the company owner supplies legal and billing details, reviews the company-specific Order Form and linked agreements, and explicitly accepts the contract before entering the platform. Invited employees join the already activated company without repeating its contract onboarding. If you need to reset your password later, use Forgot password? on the sign-in page.
  2. Upload a build from the dashboard. The first successful scan of an app is its baseline: every finding is new by definition, because there is no earlier build to diff against.
  3. Upload the next build. Now the report is a diff — new / fixed / persisting vs. the previous build of that same app — with a plain-language explanation and a concrete fix for each finding.

That is the whole loop: upload, read the delta, fix what's new. On re-upload each prior finding is reported as fixed or persisting, with anything genuinely new surfaced first.

Use Switch build in a scan report's sidebar to search the 50 most recent builds of the same app by build ID or version. The current build stays available at the top. Switching from Technical details keeps that view open when available. View full build history opens the project's searchable scan history for older builds.

Personal email services such as Gmail are not accepted. A company application does not require an invitation, but its activation is manual.

Platforms

  • Android (.apk) — static analysis of a release APK.
  • iOS (.ipa) — static analysis of a developer-exported ad-hoc or development IPA. Store-downloaded IPAs are encrypted and cannot be analyzed.

The alpha does not include dynamic analysis or GDPR/consent capture. Alpha organizations cannot request those scan layers through the dashboard, CLI, or API.

Where to next

  • Automate it in CI. Run the same scan on every pull request:

    export WARDIX_KEY="wdx_ak_…"
    npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> --wait --fail-on high
    

    Only new, non-dismissed findings can fail the gate; persisting findings never break a build, and dismissals carry forward across builds. See the CLI section for flags and gate behavior, and Upload keys for the WARDIX_KEY your pipeline needs.

Static-analysis alpha

This page describes the older invited static-analysis alpha accounts. New company applicants use the separate email registration, manual activation, company details and customer Order Form flow described in Getting started. Alpha organizations cannot request dynamic analysis or GDPR/consent capture.

Register and activate your account

  1. Give your Wardix contact the exact email address you want to use. Once it is approved, open https://app.wardix.io/register.
  2. Enter that email address and choose a password.
  3. Open the confirmation email and verify the address. On your first sign-in, read and explicitly accept the current alpha agreement. Until you accept it, your account cannot use the platform or submit scans.
  4. Create an app space for your build, or start a dashboard upload to create one as part of the first scan.

This invitation requirement applies to legacy alpha registration. New company applicants do not need an invitation, but staff must approve their application before contract review. For a later password reset, use Forgot password?. Its emailed recovery link is single-use and expires in one hour.

Documents and agreement history

The legal index provides permanent links to the documents and their previous versions. The Acceptable Use Policy, Privacy Policy and Cookies Policy are public. The General SaaS Terms and Data Processing Addendum require a verified, approved customer account; you can read them before accepting an agreement. Customer support contact details are available at wardix.io/support.

Use Cookies in the footer of either website to review or change your cookie preferences. Optional categories start off. Reject optional cookies keeps them off, Save preferences records the selected categories, and Accept all cookies enables all optional categories. Rejecting optional cookies does not prevent registration, sign-in or scanning. Preferences are remembered for 180 days in this browser and shared between wardix.io and app.wardix.io. The current alpha does not use optional analytics or marketing cookies.

Open Settings → Documents & agreements to review your own accepted versions and download a copy or acceptance receipt. A document marked “not published yet” is awaiting completed, approved wording.

Supported builds

  • Android: a release .apk.
  • iOS: a developer-exported development or ad-hoc .ipa. Store-downloaded IPAs are encrypted and cannot be analyzed.
  • Maximum artifact size: 300 MiB.

Upload from the dashboard

  1. Open or create your app space.
  2. Select an APK or IPA that meets the requirements above and start the upload.
  3. Wait for the static scan to finish, then open the report to review coverage, findings, and evidence.

Run from CLI or CI

export WARDIX_KEY="wdx_ak_…"
npx @wardix/cli@0.2.3 scan app-release.apk --wait --fail-on high --timeout 1500

Create the upload key on the app-space page and store it as the WARDIX_KEY secret in CI. The GitHub Action uses the same key and accepts artifact, key, fail-on, comment, upload-sarif, timeout, and cli-version; see GitHub Action for the complete example.

Read the result

The first successful scan is the baseline. Later scans classify findings as new, fixed, or persisting. Only new, non-dismissed findings at or above the configured threshold fail the gate. Required static coverage must be complete; incomplete or unknown coverage is an analysis failure, never a clean result.

  • Exit 0: the gate passed, or the upload was accepted without --wait.
  • Exit 1: new open findings met the threshold.
  • Exit 2: analysis failed, timed out, or completed without full required static coverage.
  • Exit 3: usage, input, or authentication error.

Known alpha limitations

  • Static analysis only; dynamic analysis and GDPR/consent capture are disabled for alpha organizations.
  • APK and developer-exported, unencrypted IPA files only. AAB files and store-downloaded IPAs are not supported.
  • Registration requires an approved email address. After email confirmation, you must separately agree to the current General SaaS Terms and Acceptable Use Policy before using the platform.
  • Use the dashboard, CLI, or GitHub Action. Dedicated Fastlane, Bitrise, and EAS integrations are outside this alpha.

Send feedback

Include:

  • the scan URL;
  • the expected result;
  • the actual result;
  • any finding that was noisy, unclear, or confusing; and
  • the next action you wanted to take but could not.

Do not send app binaries, upload keys, or credentials in feedback messages.

Scans & the delta view

A scan takes one build of your app (an Android .apk or a developer-exported iOS .ipa) and produces a set of findings. Wardix never shows you those findings as a flat list. Every scan is compared against the previous build of the same app, and each finding is classified by how it changed since then. This delta — not a raw dump — is the product. It answers the only question that matters in review: did the change I just shipped make things worse?

The first scan of an app is its baseline: there is nothing to compare against, so every finding is new. From the second build onward, findings are diffed against the prior build and land in one of three states.

The three finding states

StateMeaningEffect on a CI gate
newIntroduced by this build — not present in the previous build of the same app. This is the default view.The only state that can fail a gate — and only when the finding is not dismissed.
fixedPresent in the previous build, gone in this one. Shown as progress.Never fails a build.
persistingAlready known from an earlier build and still present. Reported for context, never re-raised as urgent.Never fails a build.

The practical consequence: a gate only breaks on regressions you introduced in this change. Pre-existing debt (persisting) is visible but never blocks a merge, so adopting Wardix on a mature app does not paint your pipeline red on day one. fixed findings let you show the delta going the right way. The new view is where you spend your attention.

You get the same three-state delta everywhere a scan surfaces: the dashboard at https://app.wardix.io, the CLI, and the CI API — same fingerprints, same classification. See The dashboard delta view, The CLI, and The CI API for each surface. Fingerprints are stable across builds, so the same underlying issue keeps its identity from one scan to the next; that stability is what makes new vs. persisting meaningful.

A typical CI invocation (Node 20+, no install; upload key in WARDIX_KEY, see Upload keys):

export WARDIX_KEY=wdx_ak_your_key_here
npx @wardix/cli@0.2.3 scan ./app-release.apk --wait

--wait polls until the scan finishes and then applies the gate: it fails the build (exit code 1) only on new, non-dismissed findings at or above the --fail-on severity threshold (default high). Persisting and dismissed findings never break the build. Without --wait the CLI uploads, registers the scan, prints its URL, and exits 0 without gating. See The CLI for the full flag set.

Dismissals carry forward

When a finding is a false positive or an accepted risk, dismiss it once. That dismissal is sticky: it carries forward across every future build of the app, keyed to the finding's fingerprint. A dismissed finding stays dismissed in the dashboard, is excluded from the CLI gate, and comes back marked as dismissed in the CI API response — so it never counts toward a gate. You never have to re-triage the same thing on the next build. Because only new, non-dismissed findings can fail a gate, dismissing a finding removes it from the set that can break a build without hiding it from view; it remains inspectable, just no longer urgent. Undismissing is equally durable and takes effect on the next scan.

Where findings come from

Findings are produced only by deterministic analyzers. The static-analysis alpha covers Android APKs and developer-exported iOS IPAs. Approved internal organizations may also have dynamic analyzers enabled; those findings use the same schema and delta model.

The AI layer never creates findings and never sets severity. Its job is to explain each finding in plain language and to write the concrete fix — nothing more. Severities and the finding set itself come from the analyzers alone. If an analyzer didn't raise it, it isn't a finding; if the analyzer rated it high, no model downgrades it. That boundary is deliberate: the delta you gate on is reproducible and trustworthy, not a model's opinion.

Upload keys

CI and other automated uploads authenticate with an upload key instead of your account credentials. A key lets a pipeline submit builds to a single app space without a login, a browser session, or any personal secret. Interactive dashboard uploads keep using your account; upload keys exist for machines.

Minting a key

Keys are created from an app space. Open the space you want uploads to land in from the dashboard (https://app.wardix.io) and mint a key on that space's page. The key is bound to that space at creation and cannot be re-pointed elsewhere.

The key is shown once, at the moment you create it:

  • It looks like wdx_ak_….
  • Wardix stores only a SHA-256 hash of the key, never the key itself.
  • Copy it immediately into your CI secret store. A lost key cannot be recovered — mint a replacement and revoke the old one.

Scope

An upload key is scoped to exactly one app space:

  • Scans it uploads land in that space, following the same delta model as any other upload — the first scan of an app is its baseline, and later builds are compared against the previous build of the same app.
  • It can read nothing else: not other app spaces, not the scans you uploaded interactively from the dashboard.
  • Use one key per space. To feed several spaces from CI, mint a separate key on each.

Using a key in CI

The CLI and CI API read the key from the WARDIX_KEY environment variable and send it to the scanner at https://scanner.wardix.io. The key never needs to touch disk — keep it in your CI provider's secret store and expose it only as an environment variable for the job.

export WARDIX_KEY="wdx_ak_…"
npx @wardix/cli@0.2.3 scan ./app/build/outputs/apk/release/app-release.apk

The CLI needs Node 20+ and no install (npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> [flags]). The upload lands in the key's app space. In GitHub Actions, map the secret into the environment:

env:
  WARDIX_KEY: ${{ secrets.WARDIX_KEY }}

For details on the upload flow and gate behavior, see CLI and CI API.

Rate limits

Requests are rate-limited per key. If a single pipeline uploads frequently enough to hit the limit, spread the work across multiple spaces — each with its own key, and each with its own window, so more keys means more aggregate throughput — or contact us to raise the limit.

Revoking a key

Revoke a key from the same app space page where it was minted. Revocation is immediate: every request made with that key fails from that moment on. Revoked keys are never reactivated — if a key is exposed or a pipeline is retired, revoke it and mint a fresh one.

The CLI

@wardix/cli uploads a build, waits for the scan, prints a delta-first report, and exits with a code your pipeline can act on. The alpha release is 0.2.3 and runs on Node 20+ with no install — npx fetches it on demand.

WARDIX_KEY=wdx_ak_… npx @wardix/cli@0.2.3 scan app-release.apk --wait --fail-on high

The command signature is:

npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> [flags]

Point it at an Android .apk or a developer-exported iOS .ipa (an ad-hoc or development export — store-downloaded IPAs are encrypted and cannot be analyzed). The upload key in WARDIX_KEY binds the scan to one app space; see Upload keys.

Gate semantics

Wardix is delta-first: every scan is compared against the previous build of the same app, and each finding is classed new, fixed, or persisting. The first scan of an app is its baseline, so everything is new.

The gate reacts only to what this build introduced. The CLI exits non-zero only when the build added a new, non-dismissed finding at or above --fail-on. Persisting findings never re-break a build — once a finding exists it cannot fail a later scan. Dismissals made in the dashboard carry forward and are honored in CI, so a triaged finding stays quiet on every future build.

Findings themselves come only from deterministic analyzers. The AI explains each finding and writes the fix; it never invents a finding and never changes a severity.

Without --wait, the CLI uploads, registers the build, prints the scan URL, and exits 0 — the gate is skipped and the scan still runs to completion server-side.

A completed scan passes the gate only when required static coverage is complete. Incomplete or unknown required coverage exits 2, even when no findings were returned, so missing analysis cannot look like a clean build.

The static-analysis alpha does not include dynamic analysis. Alpha organizations must omit --dynamic, --intent, and --consent; the server rejects dynamic requests for those organizations.

Flags

FlagDefaultWhat it does
--fail-on <severity>highLowest severity of a NEW finding that fails the build (critical | high | medium | low | info | none). "none" disables the gate — the scan still runs and reports.
--wait—Poll until the scan finishes, print the delta report, and apply the gate. Without it the CLI uploads, registers, prints the scan URL and exits 0.
--json—Machine-readable output: one JSON document on stdout, nothing else.
--sarif <file>—Write the findings as SARIF 2.1.0 to <file> (for GitHub code scanning upload). Requires --wait.
--timeout <seconds>1500Maximum time to wait for the scan to complete (only with --wait).
--dynamic—Internal capability only. Requests dynamic + GDPR/consent capture for organizations where Wardix has explicitly enabled it. Static-alpha organizations are rejected.
--intent <text>—Internal capability only. Custom navigation instruction for an enabled dynamic capture (only with --dynamic).
--consent <accept|reject>rejectInternal capability only. Consent path for an enabled dynamic capture (only with --dynamic).
--help—Show usage and exit.
--version—Print the CLI version and exit.

Environment

VariableRequiredWhat it does
WARDIX_KEYyesUpload key for one app space (wdx_ak_…), minted on the space page in the dashboard. Pass it as a CI secret — it is never written to disk or logs.
WARDIX_API_URLnoScanner API base URL. Default: https://scanner.wardix.io
WARDIX_APP_URLnoDashboard base URL used in finding deep links. Default: https://app.wardix.io

Exit codes

CodeNameMeaning
0passScan completed and no new, non-dismissed findings meet --fail-on (or --wait was not given and the upload was accepted).
1gate failedNew, non-dismissed findings at or above --fail-on. Persisting findings never cause this — only what this build introduced.
2scan failedThe analysis errored, completed without full required static coverage, or did not complete within --timeout.
3usage / auth errorBad arguments, unreadable file, missing or rejected WARDIX_KEY.

The Android pipeline and iOS pipeline sections show full CI setups end to end; the GitHub Action section wraps this CLI with a sticky PR comment and SARIF upload.

CI API

The wardix CLI is a thin client over a small HTTP API — you can drive the whole scan flow directly from any environment that speaks HTTPS (a language without a Node runtime, a custom build agent, a script). The CLI, the GitHub Action, and this API all reach the same pipeline and honor the same delta and dismissal rules.

Base URL: https://scanner.wardix.io. Authenticate every request with an upload key (see Upload keys) in an Authorization header:

Authorization: Bearer wdx_ak_…

A key is scoped to one app space. Scans you register land in that space, and the read endpoints only return scans in that space — a scan outside it reads as 404. A malformed or missing key is 401; a revoked key is 401.

The flow

A scan is created in three ordered steps, then polled to completion:

  1. Mint an upload — POST /ci/uploads with the build's filename. Returns a scan_id and a one-time signed upload URL. No scan row exists yet.
  2. Upload the binary — PUT the file to that signed URL. No auth header — the signed URL itself is the credential.
  3. Register the scan — POST /ci/scans with the scan_id. Verifies the binary landed, creates the queued scan in your key's space, and starts the pipeline.

Then poll GET /ci/scans/{scan_id} until status is completed or failed, and fetch GET /ci/scans/{scan_id}/findings once it completes.

The binary goes straight to storage in step 2, so large builds never proxy through the API. The extension you pass in step 1 (.apk or .ipa) selects the platform — Android (APK) or iOS (a developer-exported .ipa; store-downloaded IPAs are encrypted and cannot be analyzed). The static-analysis alpha accepts static work only.

POST /ci/uploads

Mint a scan_id and a one-time signed upload URL for a build.

Send the build's filename. The extension selects the analysis platform: .apk → Android, .ipa → iOS. Any other extension is rejected with 422.

curl -sS -X POST https://scanner.wardix.io/ci/uploads \
  -H "Authorization: Bearer $WARDIX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "app-release.apk"}'
{
  "scan_id": "3f2a…",
  "artifact_path": "…/3f2a….apk",
  "upload_url": "https://…storage…/signed…"
}

PUT the binary to the signed URL

Upload the binary to the signed URL returned above. This is a plain HTTP PUT of the file bytes with no Authorization header — the signed URL is single-use and is itself the credential.

curl -sS -X PUT --upload-file app-release.apk "$UPLOAD_URL"

POST /ci/scans

Register the uploaded build and start the scan.

Send the scan_id from step 1. The API verifies the binary actually landed in storage (409 if it did not — upload it first), inserts the queued scan in the key's space, and nudges the pipeline. Registering the same scan_id twice is 409. An optional filename is kept as the build's display name.

{ "scan_id": "3f2a…" }

Returns { "id": "3f2a…", "status": "queued" }.

Static-alpha organizations must omit dynamic fields. The server rejects direct dynamic requests for those organizations, including crafted API requests. The dynamic and consent fields are reserved for approved internal organizations where Wardix has explicitly enabled that capability.

GET /ci/scans/{scan_id}

Status and delta counts for a scan the key can see.

Poll until status is completed or failed. Once completed, the response carries required_coverage_state (complete, incomplete, or unknown) and the diff against the app's previous build: new_count, fixed_count, persisting_count. A scan outside the key's space is 404.

{
  "id": "3f2a…",
  "status": "completed",
  "required_coverage_state": "complete",
  "new_count": 2,
  "fixed_count": 1,
  "persisting_count": 5
}

GET /ci/scans/{scan_id}/findings

The findings for a completed scan. Returns 409 while the scan is still running — poll status first.

Each finding carries delta (new | persisting) and state (open | dismissed). Findings come only from deterministic analyzers — the AI explains them and writes fixes, but never invents a finding and never changes a severity.

Gating on the response

The delta model is the whole point: every finding is scored new, fixed, or persisting against the previous build of the same app. The first scan of an app is its baseline, so everything is new.

A faithful gate first requires status == "completed" and required_coverage_state == "complete". Treat incomplete or unknown coverage as an analysis failure, never as a clean result. With complete coverage, fail the build only on a finding whose delta is new and whose state is open, at or above your severity threshold. That is exactly what the CLI's --fail-on does. persisting findings never break a build — they were already there. Dismissals made in the dashboard carry forward as state == "dismissed" here, so a finding you have accepted or marked a false positive never re-fails CI. See Scans and the delta view for how these states are assigned, and the CLI and GitHub Action sections if you would rather not implement the loop yourself.

GitHub Action

The Wardix GitHub Action is a composite action that wraps the @wardix/cli CLI (see CLI). In one step it scans the artifact your workflow just built, keeps a single sticky comment on the pull request updated with the delta summary, optionally uploads a SARIF log to GitHub code scanning, and fails the job only when the gate fails.

It runs the same command you would run by hand, so it carries the same exit codes as the CLI: 0 gate passed, 1 gate failed, 2 scan failed, 3 usage or auth error. The scan, the sticky comment, and the SARIF upload always run to completion first — the job then exits with the CLI's code as the last step, so a gate failure never robs you of the comment or the code-scanning alerts.

Reference the action by its repository — wardixio/wardix-scan-action@v1 — and pin the major version tag v1. If you would rather not add an action, the plain CLI snippet below runs the identical scan in any CI.

Usage

name: wardix
on: pull_request
permissions:
  contents: read
  pull-requests: write     # sticky PR comment
  security-events: write   # only if upload-sarif: true
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./gradlew assembleRelease
      - uses: wardixio/wardix-scan-action@v1
        with:
          artifact: app/build/outputs/apk/release/app-release.apk
          key: ${{ secrets.WARDIX_KEY }}
          fail-on: high
          upload-sarif: "true"

Point artifact at whatever your build produced — an Android .apk or a developer-exported iOS .ipa. The gate reflects the delta against the previous build of the same app: only new, non-dismissed findings at or above fail-on can fail the job; persisting findings never break a build, and dashboard dismissals carry forward automatically. The first scan of an app is its baseline, so everything is new and the gate reads against that.

Plain CLI (no action)

The action is a convenience wrapper; the CLI is the contract. Any CI can do the same thing directly:

npx @wardix/cli@0.2.3 scan app/build/outputs/apk/release/app-release.apk \
  --wait --fail-on high --sarif wardix.sarif

Node 20+ is required and npx fetches the CLI on demand, so nothing needs to be installed. Pass the upload key as the WARDIX_KEY environment variable (it looks like wdx_ak_…; see Upload keys), never as a literal in the workflow file.

Inputs

InputDefaultWhat it does
artifact(required)Path to the built .apk or .ipa to scan.
key(required)Upload key (wdx_ak_…). Pass a repository secret, never a literal.
fail-onhighLowest new severity that fails the build: critical | high | medium | low | info | none.
commenttruePost and keep updating the sticky PR comment with the delta summary. Needs pull-requests: write.
upload-sariffalseUpload the SARIF log to code scanning. Needs security-events: write (and GitHub Advanced Security on private repos).
timeout1500Maximum seconds to wait for the scan to complete.
cli-version0.2.3Exact @wardix/cli npm version to run.

Outputs

OutputWhat it is
new-countFindings this build introduced.
fixed-countFindings gone since the previous build.
persisting-countFindings carried over from previous builds (never gate).
report-urlDashboard URL of the full report on https://app.wardix.io.
gateGate verdict: passed | failed | off (off when fail-on: none).

Sticky comment

With comment: true (the default), the action maintains one comment on the pull request rather than adding a new one per run. Each push rewrites that same comment in place with the current new / fixed / persisting counts, the gate verdict, the list of new findings (dismissed ones flagged as non-gating), and a link to the full report. Requires pull-requests: write and only runs on pull_request events.

SARIF

With upload-sarif: "true" — or the CLI's --sarif <file> in any CI — the findings snapshot is emitted as SARIF 2.1.0. Severity maps to the SARIF level, MASVS groups ride along as rule tags, and each alert deep-links to its finding on the dashboard. Stable finding fingerprints go into partialFingerprints, so an alert keeps its identity across builds; dashboard dismissals are exported as SARIF suppressions. GitHub diffs consecutive uploads itself to open and resolve alerts. Private repositories need GitHub Advanced Security for code scanning.

Internal dynamic analysis

Not included in the static-analysis alpha. Dynamic analysis and GDPR/consent capture are available only to organizations where Wardix has explicitly enabled the capability. Static-alpha organizations cannot request these scan layers through the dashboard, CLI, or API.

Static analysis reads the binary. A dynamic + GDPR/consent scan goes further: Wardix installs and runs the app on an instrumented emulator, records its real network traffic, and inspects consent behaviour — did it fire trackers before the user agreed, does it send data in cleartext, does it honour a rejection. Dynamic findings come from the same deterministic analyzers as everything else (the AI only explains them and drafts fixes — it never invents a finding and never changes a severity), and they land in the scan report under their own Dynamic and GDPR tabs, alongside the static ones, with the same new / fixed / persisting delta view: each finding is compared against the previous build of the same app, dismissals carry forward, and the first scan of an app is its all-new baseline.

Platform availability is organization-specific. Wardix confirms the enabled platform and capture scope before an internal organization uses this capability.

Run a dynamic scan

Add --dynamic to any scan:

WARDIX_KEY=wdx_ak_… npx @wardix/cli@0.2.3 scan app-release.apk --dynamic --consent reject --wait

The invocation is the standard npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> [flags] (Node 20+, nothing to install); --dynamic adds the capture step on top of the static scan for an enabled organization. WARDIX_KEY is your upload key — it looks like wdx_ak_… and is passed via the environment, never on the command line (see Upload keys).

Capture runs asynchronously on a separate worker, so --wait returns as soon as the static result is ready — it does not block on the dynamic run. The Dynamic and GDPR findings appear in the report a short while later, once the worker finishes. For the complete flag list and gate/exit-code behaviour, see the CLI and CI API sections.

Choose the consent path: --consent

--consent selects which consent behaviour the capture exercises:

  • --consent reject (default) tests the privacy-critical path: the capture rejects the app's consent prompt, then verifies the app respects that choice. Trackers that fire despite the rejection — or before any choice is made at all — become findings.
  • --consent accept captures the accepted path, so you can review what the app sends once the user opts in.

Steer navigation: --intent

The capture drives the app automatically, but some screens it cannot guess its way past — a login wall, or a specific feature you want exercised. Use --intent to describe in plain language where to go:

WARDIX_KEY=wdx_ak_… npx @wardix/cli@0.2.3 scan app-release.apk \
  --dynamic --consent reject \
  --intent "log in and open the account page" --wait

Test login credentials are configured per app in the dashboard, never passed on the command line. When your intent asks the capture to sign in, it uses the credentials saved for that app.

Request a dynamic scan from the dashboard

You do not have to use the CLI. When you upload a build in the dashboard you can request a dynamic scan for that upload directly. You can also save a reusable consent scenario — a consent stance plus a navigation intent — under the app's Scan layers settings, so every future build of that app captures the same way without re-passing the flags.

Android pipeline

Wire Wardix into your Android CI as a security gate: build the release APK, scan it with the CLI, and fail the job on new high-severity findings. The gate is delta-aware — it breaks a build only when a change introduces a new problem, never for issues that already existed.

1. Build the release APK

Wardix scans the compiled artifact your pipeline already produces — the release APK:

./gradlew assembleRelease
# artifact: app/build/outputs/apk/release/app-release.apk

If your pipeline ships an Android App Bundle (.aab), Wardix does not scan the bundle itself — point it at the universal/release APK you also build (the same APK you would sideload to smoke-test the release).

2. Open your space and mint an upload key

Open or create an app space in the dashboard (https://app.wardix.io), then mint an upload key for it (see Upload keys). The key looks like wdx_ak_…. Store it as a CI secret named WARDIX_KEY — the CLI reads it from that environment variable. Every scan made with a space's key is diffed against the previous build in that space, so use one space per app.

3. Add the gate

The CLI needs no install (Node 20+):

npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> [flags]

--wait blocks the job until the scan finishes and sets the exit code; --fail-on high makes the job fail when the scan surfaces new findings at or above the high severity threshold. The CLI uploads the build to and polls the CI API at https://scanner.wardix.io, so allow egress to that host if your runners are behind an egress allowlist. Copy-paste for your CI:

GitHub Actions:

- name: Wardix security gate
  run: npx @wardix/cli@0.2.3 scan app/build/outputs/apk/release/app-release.apk --wait --fail-on high
  env:
    WARDIX_KEY: ${{ secrets.WARDIX_KEY }}

GitLab CI:

wardix:
  stage: test
  image: node:20
  script:
    - npx @wardix/cli@0.2.3 scan app-release.apk --wait --fail-on high
  variables:
    WARDIX_KEY: $WARDIX_KEY

4. Gate behaviour

Wardix compares each scan against the previous build of the same app in the same space:

  • The job fails only on new, non-dismissed findings at or above --fail-on.
  • Persisting findings — those already present in the prior build — never break the build.
  • Dismissals you make in the dashboard carry forward across builds, so a triaged finding stays silent in CI.
  • The first scan of an app is its baseline: every finding is new, and it becomes the reference point that later builds diff against.

For a richer GitHub experience — a sticky PR comment summarising the delta and SARIF uploaded to the repository's Security tab — use the Wardix GitHub Action instead of calling the CLI directly (see GitHub Action).

iOS pipeline

Wire iOS scanning into CI the same way you wire Android: mint an upload key, run the wardix CLI on your build, and let the delta gate decide. The only real difference is the artifact — iOS ships a signed .ipa instead of an .apk, and it must be a developer-exported IPA.

What runs in the alpha. iOS static analysis is live for invited accounts. Upload a developer-exported .ipa to get deterministic findings, the delta view, and the same CI gate as Android. Dynamic analysis and GDPR/consent capture are outside the static-analysis alpha.

Upload keys, the CLI, the CI API, the GitHub Action, SARIF output, and the dashboard delta view all behave identically to the Android pipeline — see Upload keys, The CLI, GitHub Action, and Android pipeline.

1. Produce a developer-exported IPA

Wardix analyzes developer-exported IPAs — a development or ad-hoc export you build yourself. A store-downloaded IPA is encrypted (FairPlay DRM) and cannot be analyzed, so always export the build from your own toolchain.

Raw xcodebuild archive + export:

xcodebuild -scheme MyApp -archivePath build/MyApp.xcarchive archive
xcodebuild -exportArchive -archivePath build/MyApp.xcarchive \
  -exportPath build/ipa -exportOptionsPlist ExportOptions.plist
# → build/ipa/MyApp.ipa  (use a development or ad-hoc export)

Set method to development or ad-hoc in your ExportOptions.plist.

2. Mint an upload key

Create an upload key scoped to the app's space in the dashboard at https://app.wardix.io, then store it as the CI secret WARDIX_KEY (the value looks like wdx_ak_…). The CLI and CI API read the key from the WARDIX_KEY environment variable and route the scan to the matching space. See Upload keys for how keys are scoped, rotated, and revoked.

3. Add the gate

The CLI is invoked as npx @wardix/cli@0.2.3 scan <path-to.apk|.ipa> [flags] (Node 20+, no install). Point it at the .ipa; --wait blocks until the scan finishes, and --fail-on high sets the gate severity. A GitHub Actions step:

- name: Wardix security gate
  run: npx @wardix/cli@0.2.3 scan build/ipa/MyApp.ipa --wait --fail-on high
  env:
    WARDIX_KEY: ${{ secrets.WARDIX_KEY }}

The CLI talks to the CI API at https://scanner.wardix.io; no other configuration is needed. For SARIF upload, the full input/output matrix, and the composite Action, see GitHub Action and CI API.

Delta and gate semantics

iOS scans follow the same delta model as Android (see Scans & the delta view):

  • Every finding is scored against the previous build of the same app as new, fixed, or persisting.
  • The first scan of an app is its baseline — every finding counts as new.
  • Only new, non-dismissed findings at or above your --fail-on threshold can fail the gate. Persisting findings never break a build.
  • Dismissals carry forward across builds — a finding you dismiss in the dashboard stays dismissed on later builds and cannot re-fail the gate.

Findings come only from Wardix's deterministic static analyzers. The AI writes the plain-language explanation and the suggested fix; it never invents a finding and never changes a severity.

Data handling

Wardix keeps your findings, not your binaries. This section states exactly what is stored, what is discarded, and what leaves our infrastructure.

Binaries are deleted after every scan

  • The uploaded artifact (.apk or .ipa) is removed from storage as soon as analysis finishes — on success and on failure alike.
  • We keep the resulting findings and their history across builds. We do not keep your APKs or IPAs.
  • Deletion covers both the stored upload object and the scanner's temporary working directory.

Isolated, per-scan sandbox

  • Static analysis runs in an isolated, ephemeral sandbox that is created fresh for each scan and torn down afterward.
  • Your binary is treated as untrusted input: it never has access to account credentials or to any other tenant's data.
  • Each scan runs in its own slot — your binary is not co-processed with anyone else's.

Findings come only from deterministic analyzers

  • Every finding is produced by a deterministic analyzer, never by the AI.
  • The AI explains findings and drafts fixes. It never invents a finding and never changes a severity.
  • Because findings are deterministic, the same build produces the same findings — that is what makes the delta between builds (new / fixed / persisting) trustworthy.

What the AI sees

When you ask the AI to explain a finding, only that finding's metadata is sent to the configured LLM provider (Nebius AI Studio). Your binary, your source code, and your full app are never sent.

The payload is limited to:

  • the analyzer rule and finding title
  • the severity
  • a short evidence snippet
  • the package name
  • the detected framework
{
  "rule": "android_cleartext_traffic",
  "title": "Cleartext traffic permitted",
  "severity": "high",
  "evidence": "android:usesCleartextTraffic=\"true\"",
  "package": "com.example.app",
  "framework": "native-android"
}

Explanations are cached and reused across builds, so re-explaining a finding that persists into a later build does not send it to the provider again.

Internal dynamic analysis

Dynamic analysis and GDPR/consent capture are not included in the static-analysis alpha. For approved internal organizations where Wardix enables this capability, the app is installed and exercised inside an isolated capture sandbox. Dynamic findings are derived from the recorded behavior, and uploaded binaries follow the same deletion policy described above.