APIZDocs

APIZ guide

Partition One Shared API Across Clients

Use this pattern when several agents or applications need the same upstream API connection but must see different resources. APIZ keeps one API Instance and upstream credential, creates a separate Client for each consumer, and attaches a different mandatory Policy to each Client Binding.

For example, one S3 bucket can be divided by prefix:

                              one S3 API Instance
                         shared-artifacts + upstream key
                                      |
                 +--------------------+--------------------+
                 |                                         |
        Client: Acme exporter                    Client: Globex exporter
        mandatory Binding Policy                 mandatory Binding Policy
        tenants/acme/* only                      tenants/globex/* only
                 |                                         |
        Temporary Credential A                  Temporary Credential B

APIZ does not introduce a Tenant, Organization, Workspace, or Project object for this pattern. The Team owns the shared API Instance; Clients and their Binding Policies define the separate access views.

When To Use This Pattern

  • Several workloads use the same provider account or upstream credential.
  • The adapter exposes stable normalized resource fields that Policy can check.
  • Every consumer can have its own Client and Temporary Credentials.
  • Logical isolation with redacted decision evidence meets the risk requirement.

Do not use this as a substitute for separate provider accounts, credentials, buckets, or networks when the upstream service or your compliance boundary requires hard infrastructure isolation.

Build The Shared S3 Connection

Create one S3 API Instance for the shared bucket. The upstream IAM credential may cover the full bucket, but should still have no broader provider permission than APIZ needs. Leave the API Instance default_prefix empty because each Client Policy supplies a different enforced prefix.

In the Web Console, use API Instances → Create API Instance → S3. With the CLI, follow the credential-safe creation flow in the S3 Adapter guide:

apiz -o json api create \
  --adapter s3 \
  --name shared-artifacts \
  --config-json '{"provider":"aws_s3","endpoint_url":"https://s3.us-west-2.amazonaws.com","region":"us-west-2","bucket":"shared-artifacts"}' \
  --test-config-json '{"preset":"head_bucket","bucket":"shared-artifacts"}' \
  --credential-stdin

The bucket name is synthetic. Replace the endpoint, region, bucket, and write-only credential with the values for your controlled environment.

Create One Client Per Access View

In the Web Console:

  1. Create Clients named Acme exporter and Globex exporter.
  2. Add the same shared-artifacts API Instance to both Clients.
  3. Use the same route alias, such as artifacts, if that makes deployment configuration uniform. The alias does not provide isolation; Policy does.

With the CLI:

apiz -o json client create --name acme-exporter > acme-client.json
apiz -o json client create --name globex-exporter > globex-client.json

ACME_CLIENT_ID=$(jq -r '.client_id' acme-client.json)
GLOBEX_CLIENT_ID=$(jq -r '.client_id' globex-client.json)

apiz -o json client bind "$ACME_CLIENT_ID" \
  --api-instance <shared-api-instance-id> --alias artifacts > acme-binding.json
apiz -o json client bind "$GLOBEX_CLIENT_ID" \
  --api-instance <shared-api-instance-id> --alias artifacts > globex-binding.json

Define One Prefix Policy Per Client

Create two Structured Policies from the same definition. Change only the name, reason, and allowed prefix. This Acme example allows object reads and prefix listing under tenants/acme/; every other action or resource is denied.

{
  "type": "structured",
  "adapter_scope": "s3",
  "when": {
    "actions": ["s3:list_objects", "s3:get_object", "s3:head_object"],
    "conditions": [
      {"field": "resource.bucket", "op": "equals", "value": "shared-artifacts"},
      {"field": "resource.prefix", "op": "prefix", "value": "tenants/acme/"}
    ]
  },
  "then": {"decision": "allow", "reason": "acme_prefix_read_allowed"},
  "otherwise": {"decision": "deny", "reason": "outside_acme_partition"}
}

For Globex, replace tenants/acme/ with tenants/globex/ and use distinct stable reasons. S3 normalization supplies the requested list prefix for list operations and the object key for object operations as resource.prefix. A bucket-wide list with no prefix therefore does not match either Policy.

In the Web Console, build these conditions in the Structured Policy form. With the CLI, use two Policy Project directories and follow the publish lifecycle in the Structured Policy tutorial.

Prove Both Sides Of The Boundary

Each Policy needs credential-free fixtures for its own prefix and the adjacent prefix. For Acme, test at least:

RequestExpected result
List shared-artifacts with prefix tenants/acme/allow: acme_prefix_read_allowed
Get shared-artifacts/tenants/acme/report.jsonallow: acme_prefix_read_allowed
List with prefix tenants/globex/deny: outside_acme_partition
Get shared-artifacts/tenants/globex/report.jsondeny: outside_acme_partition
Delete an Acme objectdeny: outside_acme_partition
List the bucket without a prefixdeny: outside_acme_partition

Repeat the mirrored suite for Globex. Publish the exact definitions and fixtures, run both published suites, enable the Policies, and attach each one to only its matching Client Binding. Keep the bindings mandatory and at a stable execution order such as 100.

ACME_BINDING_ID=$(jq -r '.client_binding_id' acme-binding.json)
GLOBEX_BINDING_ID=$(jq -r '.client_binding_id' globex-binding.json)

apiz client binding policy add "$ACME_BINDING_ID" \
  --policy <acme-policy-id> --order 100
apiz client binding policy add "$GLOBEX_BINDING_ID" \
  --policy <globex-policy-id> --order 100

Do not attach both tenant Policies to the same Binding: cumulative mandatory Policies would require both decisions to allow and would deny every partition.

Issue Separate Temporary Credentials

Issue credentials independently so one Client can be revoked without affecting the other:

apiz -o json client credentials create "$ACME_CLIENT_ID" \
  --binding artifacts --ttl 1h --format aws-credentials > acme-setup.json
apiz -o json client credentials create "$GLOBEX_CLIENT_ID" \
  --binding artifacts --ttl 1h --format aws-credentials > globex-setup.json

An Acme agent may use the generated AWS-shaped APIZ credential and endpoint:

aws --endpoint-url "$APIZ_S3_ENDPOINT" s3api list-objects-v2 \
  --bucket shared-artifacts --prefix tenants/acme/

The same credential requesting tenants/globex/ is denied before Secret Broker release and before S3 contact. It never receives the shared upstream access key. Globex receives a separate Temporary Credential with the opposite view.

Verify Isolation And Evidence

Open Logs / Audit → Access Logs and compare an allowed in-prefix request with a denied cross-prefix request. Confirm:

  • the credential resolved the expected Client and Binding;
  • S3 normalized the expected action, bucket, key, and prefix;
  • the matching Published Policy Revision produced the stable reason;
  • denied cross-prefix traffic did not reach upstream; and
  • evidence contains no APIZ credential or upstream access-key value.

Use Audit Events to confirm who changed either Policy or attached it to a Binding. Revoke only the affected Client's Credential Group when one consumer is compromised; disable the shared API Instance only when every partition must stop.

Safety Boundaries

  • A configured default_prefix is a routing/test convenience, not an authorization boundary. Enforce the prefix in Policy.
  • Require a prefix on list operations. Otherwise a bucket-wide listing can reveal object names from other partitions.
  • Include read, write, multipart, tagging, copy, and delete actions explicitly when a tenant needs them; never assume one object action covers the others.
  • Keep a global mandatory API Instance Policy for rules that every Client must obey, such as denying bucket administration. Client Binding Policies provide the per-consumer slice.
  • A Policy misconfiguration can widen a logical partition. Maintain allow, adjacent-deny, dangerous-action, and no-prefix fixtures for every partition.
  • Prefer separate upstream credentials or resources when compromise of one logical partition must be cryptographically or operationally independent.