Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/dev/llms.txt
Full corpus: https://docs.registrystack.org/dev/llms-full.txt

# Package a lookup over a registry

> Export one Base Registry Engine lookup as a fixed Evidence source, author assertions over it, package a candidate offline, and know how to check what the source credential reaches once the registry is deployed.

import QuickstartMeta from '../../../components/QuickstartMeta.astro';

If your institution keeps records in a Base Registry Engine (BReg) registry, Evidence can use one
of its lookup routes as a fixed source and sign a narrow answer about the record it finds. You
start a new project here, not one from either first tutorial. Evidence inherits none of the
registry's authorization: the registry decides what the source credential reads, and Evidence
decides who may ask.

<QuickstartMeta
  outcome="A checked Evidence candidate with two Sources, three AssertionDefinitions, and 35 synthetic fixture cases."
  time="About 20 minutes after installing matching binaries"
  level="Offline authoring with synthetic data"
  prerequisites={['Matching bregctl, evidencectl, and evidence binaries on PATH', 'A shell, tar, yq version 4, and an editor', 'The downloaded BReg and Evidence teaching archive']}
/>

## Unpack the teaching registry

Download the [BReg and Evidence teaching files](../../examples/breg-evidence-starter.tar.gz)
into a fresh working directory as `breg-evidence-starter.tar.gz`, then unpack them and check the
registry:

```sh
mkdir teaching-inputs
tar -xzf breg-evidence-starter.tar.gz -C teaching-inputs
cp -R teaching-inputs/registry ./registry
bregctl check ./registry
```

The registry has one Record type, `record`, with two unique selectors, `by-code` and
`by-registration-number`. Its `evidence-source` access profile may only look records up, and
cannot read `label`. The two `breg.access.profile-unrestricted-rows` warnings are expected: both
profiles set [`rowBoundaries: unrestricted`](../../configure/breg-access/#grant-fields-and-rows).

The archive also holds `offline-starter`, a complete Evidence project, and `starter`, a fragment
of assertion definitions. Start the project from the first:

```sh
evidencectl init ./evidence --starter ./teaching-inputs/offline-starter --profile local
evidencectl test ./evidence --target ./evidence/local-target.yaml
```

```text test-excerpt
PASS: fixtures/extract-record-active.yaml (13 cases)
```

Those 13 cases prove a SQLite-extract Source, not registry access.

{/* Evidence: docs/site/scripts/generate-breg-evidence-starter.mjs;
    products/breg/evidence/registry/registry.yaml;
    crates/registry-breg/src/access.rs, access_findings;
    products/breg/evidence/offline-starter/evidence.yaml;
    products/breg/evidence/starter/README.md. */}

## Export the lookup as a source

The registry writes the source description, so the lookup Evidence calls is the one the
registry reviewed:

```sh
bregctl generate evidence-source ./registry \
  --access-profile evidence-source --record-type record \
  --selector by-code --selector by-registration-number \
  --fields status --source-id registry-status --connection registry \
  --output ./registry-status-export
```

It reports `Generated 9 artifacts.` The export names the profile, both selectors, and the
`status` fact, and grants no registry authority. Asking for `label` is refused with
`breg.evidence-source.refused`: the export describes only what the registry already lets that
profile see.

Import it into the project:

```sh
evidencectl source import ./registry-status-export --project ./evidence
```

```json test-excerpt
{
  "affectedDefinitions": [],
  "application": "accepted",
  "conflicts": [],
  "sources": ["registry-status"]
}
```

The import checks every artifact's checksum, then adds the `registry-status` Source, its selector
profiles, and two Functions. It adds no assertion definition and no caller grant: the registry's
profile does not become an Evidence permission. For a local registry with a stopped development
session, `evidencectl source add` does both steps, driving the `bregctl` of the same version on
your `PATH`.

{/* Evidence: crates/registry-breg/src/evidence_source.rs;
    crates/registry-evidencectl/src/source_import/mod.rs, replace_project_sections;
    crates/registry-evidencectl/src/source_add.rs, check_public_bregctl. */}

## Add the assertion definitions

Copy the fragment's Functions and fixtures, then merge its definitions with
[yq](https://github.com/mikefarah/yq) version 4:

```sh
cp teaching-inputs/starter/functions/*.rhai evidence/functions/
cp teaching-inputs/starter/fixtures/*.yaml evidence/fixtures/
yq --inplace '
  .functions += load("teaching-inputs/starter/definitions.yaml.fragment").functions
  | .assertionDefinitions += load("teaching-inputs/starter/definitions.yaml.fragment").assertionDefinitions
' evidence/evidence.yaml
yq '.assertionDefinitions | keys' evidence/evidence.yaml
```

```text test-expect
- extract-record-active
- record-active
- record-status-pair
```

`record-active` derives one boolean and `record-status-pair` the active and retired pair, both
through the imported Source. Who may request them is Evidence's grant ceiling and authority, reviewed
in this project, not anything the registry granted.

{/* Evidence: products/breg/evidence/starter/definitions.yaml.fragment;
    products/breg/evidence/starter/functions/record-active.rhai;
    products/breg/evidence/starter/functions/record-status-pair.rhai. */}

## Bind the registry connection and package

Create a target for the project:

```sh
evidencectl target new ./configured-target --local --project ./evidence
```

```text test-expect
Created Evidence target ./configured-target
```

Bind the `registry` connection the export named. Its credential is the source client's own,
issued by the registry's identity provider; an Evidence caller never holds it:

```diff lang="yaml" title="configured-target/target.yaml" test-edit
-connections: {}
+connections:
+  registry:
+    origin: http://127.0.0.1:8090
+    authentication:
+      type: oauth2-private-key-jwt
+      tokenEndpoint: http://127.0.0.1:8091/oauth2/token
+      clientIdRef: secret:file/registry-client-id
+      clientAssertionKeyRef: secret:file/registry-client-key
+      clientAssertionAudience: http://127.0.0.1:8091
+      scope: registry:evidence:lookup
+      maximumCacheSeconds: 60
```

Fixtures supply recorded responses, so nothing contacts these endpoints; bind the real origin,
issuer, audience, scope, and secrets before live use. Prove the project and package it:

```sh
evidencectl test ./evidence --target ./configured-target
evidencectl package ./evidence --target ./configured-target --output ./candidate
```

```text test-excerpt
4 passed, 0 failed (35 cases evaluated)
```

The package report lists the secrets to provision, the two registry client secrets among them.

{/* Evidence: crates/registry-evidencectl/src/target.rs, create_local_target;
    crates/registry-evidencectl/src/build.rs, run_inner;
    products/breg/evidence/tests/verify-composition.py, verify. */}

:::note[Once the registry is deployed, check what the source credential reaches]
Fixtures cannot show what a live credential reaches. Run this check against your [deployed
registry](../../operate/breg/) once it serves this project, and before Evidence takes traffic,
calling it as Evidence will, with the source client's token. Ask your identity operator for an owner-only
`source-authorization.curl` file holding a short-lived authorization header for the source
client, kept outside the candidate. Put the synthetic selector in `source-lookup.json`:

```json
{"selector":"by-code","values":{"code":"SYNTHETIC-ACTIVE"}}
```

Look the record up through the route the export names:

```sh
curl --silent --show-error --fail-with-body \
  --config ./source-authorization.curl \
  --header 'Content-Type: application/json' \
  --data-binary @source-lookup.json \
  'https://registry.example.invalid/v1/records/records:lookup?accessProfile=evidence-source&$select=code,status' \
  --output source-lookup-response.json --write-out 'HTTP %{http_code}\n'
```

A match answers HTTP 200 with one record, limited to `code` and `status`; read its UUID from
`data.recordIdentifier`.

Then ask for the same record through the `operator` profile, which the source client does not
hold. Replace `<recordIdentifier>` with the UUID:

```sh
curl --silent --show-error --fail-with-body \
  --config ./source-authorization.curl \
  'https://registry.example.invalid/v1/records/records/<recordIdentifier>?accessProfile=operator' \
  --output source-refusal.json --write-out 'HTTP %{http_code}\n'
```

This one must fail with HTTP 404 and the problem `resource.not_found`, which is also how the
registry answers a missing record, so ask the registry operator to confirm with their own
credential, in their own workspace, that the record exists under `operator`. [Read
refusals](../../reference/breg-api/#read-refusals) lists these answers. If the request succeeds,
keep traffic off and correct the source client or the profile before you retry.
:::

{/* Evidence: crates/registry-breg/src/compiler.rs, route_shape;
    crates/registry-breg/src/api/mod.rs, parse_lookup_body, lookup_dispatch, lookup_unresolved,
    authorize_direct_route, authorize_profile_claims, audited_read_concealment and concealed;
    crates/registry-breg/src/problem.rs, ResourceNotFound and LookupUnresolved;
    crates/registry-breg/tests/http_read_only.rs,
    lookup_route_conceals_a_substituted_access_profile_selection;
    crates/registry-record/src/lib.rs, decode_record;
    products/breg/evidence/registry/registry.yaml. */}

## Keep the two reviews separate

A changed selector, fact, or profile needs a fresh export, a reviewed `evidencectl source diff`
and `source update`, and a new package. [How Evidence works](../../explanation/how-evidence-works/) explains the split.

{/* Evidence: products/breg/EVIDENCE.md;
    products/evidence/reference/authoring-projects/SOURCE-EXPORT.md. */}

## Next

[Prove a Registry Evidence project](../prove-an-evidence-project/)