> For the complete documentation index, see [llms.txt](https://docs.telys.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.telys.ai/security/runtime-signature-verification.md).

# Verify the runtime

Verify a Telys install offline two ways: the one-command CLI check to run after every install or upgrade, and the `telys.verify` API to gate the runtime in your own startup. Both run entirely offline, against public keys embedded in the wheel — no network, no key server.

## Before you start

You need an SDK install and a provisioned runtime:

```bash
pipx install telys        # or: pip install telys
telys login               # provisions the signed runtime + license
```

The signed bundle lands under `$TELYS_HOME/runtime/<platform>/` (default `~/.telys/runtime/<os>-<arch>/`). Everything below reads from there.

## Verify with the CLI

{% stepper %}
{% step %}

#### Run the offline verification

```bash
telys runtime verify
echo $?
```

`telys runtime verify` performs the full three-anchor check — release-manifest signature, per-artifact SHA-256, and offline license — and exits `0` only when all three pass. No network required.
{% endstep %}

{% step %}

#### Read the exit code in scripts

```bash
if telys runtime verify; then
  echo "runtime trusted"
else
  echo "verification failed — refusing to run" >&2
  exit 1
fi
```

A non-zero exit means an anchor failed; treat it as fail-closed and do not fall back to an unverified runtime.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
There is no standalone `telys verify` command. Runtime verification is `telys runtime verify` plus the `telys.verify` API below. `telys runtime status` reports only whether a runtime is *installed* (exit `0`/`1`); `verify` proves it is *genuine*.
{% endhint %}

## Verify with the API

The same checks import from `telys.verify`, so you can gate your own startup. `verify_manifest` and `verify_license` take inputs you read straight from the bundle; `verify_artifact` compares a file's SHA-256 to the value in the trusted manifest.

{% code title="verify\_runtime.py" lineNumbers="true" %}

```python
import datetime
import glob
import os

from telys.verify import (
    release_public_key,
    license_public_key,
    verify_manifest,
    verify_artifact,
    verify_license,
    sha256_file,
)

# Locate the single verified-install directory under $TELYS_HOME/runtime/.
home = os.environ.get("TELYS_HOME", os.path.expanduser("~/.telys"))
runtime_dir = glob.glob(os.path.join(home, "runtime", "*"))[0]

# The keys the SDK trusts (from the embedded PEMs).
release_pub = release_public_key()
license_pub = license_public_key()

# 1. Release manifest -> trusted manifest dict (RS256 / PKCS1v15 / SHA-256).
with open(os.path.join(runtime_dir, "telys_manifest.json"), "rb") as f:
    manifest_bytes = f.read()
with open(os.path.join(runtime_dir, "telys_manifest.sig"), "rb") as f:
    signature = f.read()
manifest = verify_manifest(manifest_bytes, signature)   # raises if tampered
print("manifest OK:", manifest.get("version"))

# 2. Artifact integrity -> compare each library's SHA-256, in constant time,
#    to the value the verified manifest recorded for it.
lib = os.path.join(runtime_dir, "libty_runtime.dylib")   # .so on Linux
verify_artifact(lib, expected_sha256)                    # from the manifest
print("artifact OK:", sha256_file(lib)[:16], "…")

# 3. Offline license -> claims dict; strict, no network, pins RS256, ver == 2.
with open(os.path.join(runtime_dir, "license.jwt")) as f:
    license_jwt = f.read().strip()
now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())  # verify_license wants a Unix int
claims = verify_license(license_jwt, now=now)
print("license OK:", claims["products"]["telys"])        # tier + features
```

{% endcode %}

The three functions map one-to-one onto the anchors:

| Function          | Signature                                                                                         | Checks                                                                                                               |
| ----------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `verify_manifest` | `verify_manifest(manifest_bytes, signature, pubkey=None) -> dict`                                 | RS256 / PKCS#1 v1.5 + SHA-256 over the exact manifest bytes; returns the trusted manifest.                           |
| `verify_artifact` | `verify_artifact(path, expected_sha256)`                                                          | Recomputes SHA-256 and compares, in constant time, to the expected value; raises on mismatch.                        |
| `verify_license`  | `verify_license(token, pubkey=None, *, now, issuer=None, audience=None, product="telys") -> dict` | Strict offline RS256; pins alg, rejects `crit`, checks `iss`/`aud`/`ver == 2`/`exp` + grace, reads `products.telys`. |

{% hint style="info" %}
`verify_manifest` and `verify_license` have well-defined external inputs. `verify_artifact` needs the expected SHA-256 from inside the trusted manifest; for the full artifact walk, prefer `telys runtime verify`, which iterates every artifact for you.
{% endhint %}

## Expected result

`telys runtime verify` exits `0` on a genuine install:

```bash
telys runtime verify
echo $?          # 0 = signature, hashes, and license all valid
```

The API script prints one confirmation per anchor:

```
manifest OK: 0.1.0b2
artifact OK: 9f2a1c7b4e6d8a03 …
license OK: {'tier': 'telys_developer', 'features': [...]}
```

If verification fails, the CLI exits non-zero and the API raises: a tampered manifest fails at step 1, a swapped library at step 2, an expired or wrong-product license at step 3. See [**Signature verification failures**](/troubleshooting/signature-verification.md).

## Override the trust anchors

Point verification at keys of your own — for example a staging release key. These environment variables override the embedded defaults:

| Variable                                             | Overrides                                                           |
| ---------------------------------------------------- | ------------------------------------------------------------------- |
| `TELYS_RELEASE_PUBKEY` / `TELYS_RELEASE_PUBKEY_FILE` | The release key used by `verify_manifest` / `release_public_key()`. |
| `TELYS_LICENSE_PUBKEY` / `TELYS_LICENSE_PUBKEY_FILE` | The license key used by `verify_license` / `license_public_key()`.  |
| `TELYS_LICENSE_ISSUER`                               | The expected license `iss`.                                         |
| `TELYS_LICENSE_AUDIENCE`                             | The accepted license `aud` (comma-separated).                       |

```bash
# Point verification at your own PEM (e.g. a staging release key).
export TELYS_RELEASE_PUBKEY_FILE=/path/to/staging_release_pub.pem
telys runtime verify
```

{% hint style="warning" %}
Overriding the keys means trusting whoever holds the matching private key. In production, leave the embedded PEMs and default issuer/audience in place — they are the trust anchors Telys ships.
{% endhint %}

## Next steps

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Three trust anchors</strong></td><td>The cryptography behind each check.</td><td><a href="/pages/IfZ7h0ml3OjlVuNi4tGX">/pages/IfZ7h0ml3OjlVuNi4tGX</a></td></tr><tr><td><strong>Verify your install</strong></td><td>The quick version, status, and verify commands.</td><td><a href="/pages/h4z9ZPSmvEnW3qsGotXt">/pages/h4z9ZPSmvEnW3qsGotXt</a></td></tr><tr><td><strong>telys runtime</strong></td><td>Every <code>runtime</code> subcommand and its exit codes.</td><td><a href="/pages/5Vd47RwOO39CSEHoL96V">/pages/5Vd47RwOO39CSEHoL96V</a></td></tr><tr><td><strong>Signature verification failures</strong></td><td>Diagnose a failed anchor.</td><td><a href="/pages/0eykLEyLKnupmpNlVgH9">/pages/0eykLEyLKnupmpNlVgH9</a></td></tr></tbody></table>

Next: understand what each check proves in [**Three trust anchors**](/security/trust-anchors.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.telys.ai/security/runtime-signature-verification.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
