> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensor9.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CI/CD Automation Guide

> Add Cloud Adapter tests and deployments to a pipeline without hiding endpoint, identity, or cleanup risk.

A pipeline is an operating pattern, not a separate compatibility layer. Choose one tested client or infrastructure tool, pin it, and make the Cloud Adapter endpoint explicit at the job boundary.

## Prove the command locally first

Before moving the workflow into CI, start one adapter interactively and verify its target effect. The bounded example uses S3 backed by Google Cloud Storage:

**1. Install or update the Tensor9 CLI**

Install the customer CLI with Homebrew, then confirm the binary is available:

```bash theme={null}
brew tap tensor9ine/tensor9
brew trust tensor9ine/tensor9
brew install tensor9
tensor9 --version
```

If `tensor9` is already installed, run `brew upgrade tensor9` instead of the install command.

**2. Authenticate with Tensor9**

To create a Tensor9 account and download an API key yourself, follow [Get a Tensor9 API key](https://docs.tensor9.com/byoc/getting-started/api-key.md).

A Tensor9 API key is required. Use the key issued during onboarding, or email [hello@tensor9.com](mailto:hello@tensor9.com) to request one for your organization. Do not share your account or key with another customer or organization.

If your secret manager has already supplied `T9_API_KEY`, skip the prompt. Otherwise, enter your key at a hidden prompt in the terminal where you will run `tensor9`:

> [tab] Bash
>
> ```bash theme={null}
> set +x
> IFS= read -r -s -p 'Tensor9 API key: ' T9_API_KEY
> printf '\n'
> export T9_API_KEY
> ```

> [tab] Zsh
>
> ```zsh theme={null}
> set +x
> IFS= read -r -s 'T9_API_KEY?Tensor9 API key: '
> printf '\n'
> export T9_API_KEY
> ```

Check that the variable is set without printing it, then verify your Tensor9 identity:

```bash theme={null}
: "${T9_API_KEY:?Set T9_API_KEY before continuing}"
tensor9 whoami
```

Confirm that the reported identity belongs to the intended customer account. Do not continue if authentication fails or the account is wrong. While `T9_API_KEY` is set, `tensor9` commands in this terminal use the key instead of any controller profile, so use a separate terminal for this work and run `unset T9_API_KEY` when you finish. Keep shell tracing disabled while using the key. Do not paste the key into commands, shell profiles, source files, screenshots or support messages.

See [Install and Authenticate](https://docs.tensor9.com/cloud-adapter-early-access/getting-started/install-and-authenticate.md) for the planned license lifecycle, entitlements and credential boundaries.

**3. Inspect the S3 to Google Cloud Storage mapping**

Ask Tensor9 to describe the exact directed service mapping used by this guide:

```bash theme={null}
tensor9 explain \
  -origin aws::1.0.0::s3 \
  -target google::1.0.0::gcs \
  -fmt Human
```

Read the supported S3 operations and Google Cloud Storage limitations before starting the process. The mapping is directional: this command describes S3 requests adapted to Google Cloud Storage, not the reverse direction.

**4. Prepare Google Cloud access**

Authenticate with Google Cloud using its standard Application Default Credentials chain. The adapter uses that identity when it calls Google Cloud Storage.

Use a dedicated test project and narrowly scoped credentials. Do not put credentials in the command line, source code, or screenshots.

**5. Start the S3 service adapter**

Run the S3 to Google Cloud Storage adapter as a foreground process:

```bash theme={null}
tensor9 adapt svc run \
  --origin aws::1.0.0::s3 \
  --backend google::1.0.0::gcs
```

Leave this terminal open. This is the current foreground launcher, not the pending Docker launcher for [local development mode](https://docs.tensor9.com/cloud-adapter-early-access/deployment/single-binary.md). It calls your actual target-cloud services; it is separate from the in-memory emulator. Acquisition and license renewal require Tensor9 authentication. Keep the endpoint local to your test.

**6. Copy the listening endpoint**

When the command reports its listening endpoint, copy that exact value into a second terminal:

```bash theme={null}
export T9_CLOUD_ADAPTER_ENDPOINT="<endpoint printed by tensor9>"
```

Keep the endpoint local to the test unless you have deliberately configured network access and authentication for another machine.

**7. Run one bounded S3 request**

Continue with this page's client or tool instructions. Point only S3 at `T9_CLOUD_ADAPTER_ENDPOINT`, then perform one operation whose Google Cloud Storage effect you can verify independently.

Check both sides: read the S3-shaped response and inspect the native object or bucket in Google Cloud Storage. A successful client response alone does not prove that the target state is correct.

**8. Stop the local adapter**

Return to the first terminal and press **Control-C**. Confirm the process exits before deleting the test object or bucket with Google Cloud's native client.

If the request failed, preserve the request ID, adapter endpoint, S3 operation name, Google Cloud request ID, and any `x-t9-explain-*` receipt headers before stopping the process.

## Pipeline contract

Define these inputs before writing YAML:

| Input | Purpose |
| - | - |
| `T9_API_KEY` | Tensor9 customer identity for authenticated acquisition, Explain and license renewal jobs. |
| Adapter endpoint | The origin API endpoint under test. |
| Origin region | Signing and provider behavior expected by the application. |
| Origin identity | Dedicated pipeline identity accepted by the adapter. |
| Test namespace | Unique bucket, prefix, stack, or resource names for the run. |
| Target verification identity | Read-only native access used to verify target state. |
| Adapter and tool versions | Evidence needed to reproduce the run. |

Target runtime credentials belong to Cloud Adapter. Do not copy them into the build system.

Store `T9_API_KEY` in the CI provider's encrypted secret store and inject it only into jobs that run authenticated `tensor9` commands. Do not put its value in workflow YAML, print it, or pass it as a command-line option. A request-only smoke test against an already running adapter does not need the key unless the job also invokes Tensor9 tooling.

## Recommended stages

1. **Validate:** pin and print non-secret tool versions; validate configuration offline.
2. **Read-only smoke test:** prove DNS, TLS, authentication, adapter routing, and one covered read.
3. **Preview:** produce a plan or preview artifact for review.
4. **Approval:** require a human or policy gate before mutation in shared environments.
5. **Apply:** create one uniquely named resource with bounded timeouts and concurrency.
6. **Verify:** check both the origin-shaped response and native target state.
7. **Expected failure:** exercise one not-found or validation path and retain request identifiers.
8. **Cleanup:** remove only resources carrying the run's unique namespace.
9. **Publish evidence:** versions, redacted configuration, request IDs, verification result, and cleanup result.

## Example authenticated entitlement-check job

> [!NOTE]
>
> Before running a `tensor9` command, [install the CLI, set `T9_API_KEY`, and verify your customer account](https://docs.tensor9.com/cloud-adapter-early-access/getting-started/install-and-authenticate). Online commands are authenticated and their adapter detail is limited to mappings your account is entitled to use.

Keep the Tensor9 key on the one step that calls authenticated Tensor9 services. The other steps in this job do not inherit it:

```yaml theme={null}
cloud-adapter-entitlement-check:
  runs-on: ubuntu-latest
  timeout-minutes: 5
  steps:
    - name: Install tensor9
      run: curl -sSL https://t9-artifacts-prod-1.s3.us-west-2.amazonaws.com/install-latest.sh | sh
    - name: Verify identity and entitled direction
      env:
        T9_API_KEY: ${{ secrets.T9_API_KEY }}
      run: |
        set +x
        tensor9 whoami
        tensor9 adapt svc list -origin aws::1.0.0::s3 -fmt Human
```

## Example request-only smoke-test job

```yaml theme={null}
cloud-adapter-smoke:
  runs-on: ubuntu-latest
  timeout-minutes: 10
  concurrency: cloud-adapter-smoke
  env:
    AWS_ENDPOINT_URL_S3: ${{ vars.T9_CLOUD_ADAPTER_ENDPOINT }}
    AWS_REGION: us-east-1
    TEST_BUCKET: ${{ vars.ADAPTER_TEST_BUCKET }}
    AWS_ACCESS_KEY_ID: ${{ secrets.ADAPTER_ORIGIN_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.ADAPTER_ORIGIN_SECRET_ACCESS_KEY }}
    AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
  steps:
    - name: Record client version
      run: aws --version
    - name: Create a unique key
      run: echo "TEST_KEY=ci/${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.txt" >> "$GITHUB_ENV"
    - name: Write the smoke-test object
      run: |
        printf 'hello through Cloud Adapter\n' > /tmp/cloud-adapter-smoke.txt
        aws s3api put-object --bucket "$TEST_BUCKET" --key "$TEST_KEY" \
          --body /tmp/cloud-adapter-smoke.txt --content-type text/plain \
          --region "$AWS_REGION"
    - name: Read and compare the object
      run: |
        aws s3api get-object --bucket "$TEST_BUCKET" --key "$TEST_KEY" \
          --region "$AWS_REGION" \
          /tmp/cloud-adapter-downloaded.txt
        cmp /tmp/cloud-adapter-smoke.txt /tmp/cloud-adapter-downloaded.txt
    - name: Remove the smoke-test object
      if: always()
      run: |
        aws s3api delete-object --bucket "$TEST_BUCKET" --key "$TEST_KEY" \
          --region "$AWS_REGION"
```

This job is a complete origin-side lifecycle. `AWS_ENDPOINT_URL_S3` routes only S3 clients in the job to Cloud Adapter; it does not redirect other AWS services. Add a target-native, read-only verification step from the matching route quickstart before cleanup when the pipeline has a target verification identity. Use the CI system's workload identity or secret store for origin credentials. Never echo environment variables or enable shell tracing around credential and signed-request setup.

## Retry and concurrency rules

* Bound automatic retries. A timed-out mutation may already have reached the target.
* Serialize tests that share a fixed resource name.
* Prefer a unique prefix per run and exact cleanup over broad bucket deletion.
* Make cleanup idempotent and run it after partial failure.
* Keep verification read-only and independent from the origin client when possible.

## Diagnostics artifact

Include operation name, timestamp, tool version, adapter endpoint hostname, adapter version, HTTP status, origin request ID, target request ID when available, and whether target state changed. Exclude authorization headers, signed URLs, credential material, request bodies, and unredacted configuration.

Base the job on a concrete guide such as [AWS CLI](https://docs.tensor9.com/cloud-adapter-early-access/guides/aws-cli.md) or [Terraform/OpenTofu](https://docs.tensor9.com/cloud-adapter-early-access/guides/terraform-opentofu.md), and review [configuration transitions](https://docs.tensor9.com/cloud-adapter-early-access/configuration/overview.md#treat-changes-as-transitions) for change and recovery boundaries.

The included [Support Agents](https://docs.tensor9.com/cloud-adapter-early-access/support/agents.md) can investigate that reviewed diagnostics artifact. Give the agent the failing stage, exact tool and adapter versions, selected mapping, resource scope and unresolved question. Retain CI's existing approval and credential controls; analyzing an artifact does not authorize a rerun, deployment or cleanup.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.