Skip to main content

Overview

Use this guide for a single Apple Silicon Mac without MDM. Beacon’s object storage forwarders are harness-neutral: they tail the system endpoint logs, so Claude Code, Codex CLI, Cursor, VS Code, opencode, and other configured integrations can share the same S3 or GCS pipeline. The end state is:
  • Beacon and bundled Vector are installed under /opt/beacon.
  • com.beacon.endpoint.collector receives local OTLP telemetry.
  • Supported hooks and plugins write to the same system runtime log.
  • Runtime activity is stored in /var/log/beacon-agent/runtime.jsonl.
  • Inventory telemetry is stored in /var/log/beacon-agent/inventory_state.jsonl.
  • Vector forwards both streams to S3, GCS, or both:
<bucket>/<prefix>/runtime/date=YYYY-MM-DD/<timestamp>-<uuid>.jsonl.gz
<bucket>/<prefix>/inventory/date=YYYY-MM-DD/<timestamp>-<uuid>.jsonl.gz
The scripts below use the destination-only install-forwarder.sh helpers. The repair-hooks-and-forwarder.sh scripts under the claude/ package path are Claude convenience wrappers: they install the same forwarder and then repair Claude hooks. The forwarding services themselves do not inspect or depend on the producing harness.

1. Install Beacon

Download and install the latest signed Apple Silicon endpoint package from GitHub Releases:
tmpdir="$(mktemp -d)"
gh release download \
  --repo Asymptote-Labs/agent-beacon \
  --pattern 'BeaconEndpointAgent-*-arm64.pkg' \
  --dir "$tmpdir"

sudo installer -pkg "$tmpdir"/BeaconEndpointAgent-*-arm64.pkg -target /
/opt/beacon/bin/beacon version
The package installs Beacon, the collector, bundled Vector, and the S3 and GCS forwarder helpers. Confirm the system endpoint is healthy:
sudo /opt/beacon/bin/beacon endpoint status --system
sudo launchctl print system/com.beacon.endpoint.collector

2. Configure Runtime Harnesses

Object storage forwarding begins after events reach Beacon’s system logs. Configure the runtimes you use before troubleshooting the destination. The package configures the default native OTLP path for Claude Code and Codex CLI for the active console user. Other hook- or plugin-backed integrations use their normal endpoint installation commands. For example:
/opt/beacon/bin/beacon endpoint hooks install \
  --harness cursor \
  --level user \
  --log-path /var/log/beacon-agent/runtime.jsonl

/opt/beacon/bin/beacon endpoint hooks install \
  --harness opencode \
  --level user \
  --log-path /var/log/beacon-agent/runtime.jsonl
Run hook installers as the interactive user, not root. Review Agent Harness Integrations and Endpoint Hooks for the supported setup method for each runtime. Restart the runtime after changing hooks or OTLP settings. Confirm recent local events before configuring a cloud destination:
sudo /opt/beacon/bin/beacon endpoint status --system --json
ls -l \
  /var/log/beacon-agent/runtime.jsonl \
  /var/log/beacon-agent/inventory_state.jsonl

3. Choose A Destination

Use a root prefix such as beacon-prod. Do not include runtime or inventory; Beacon appends those folders.

AWS S3

You need an AWS account where your current AWS CLI identity can create an S3 bucket, IAM user, and inline policy. Confirm the identity before making changes:
aws sts get-caller-identity
Choose globally unique bucket and writer names:
export AWS_REGION="us-west-2"
export BEACON_S3_BUCKET="<globally-unique-bucket>"
export BEACON_S3_PREFIX="beacon-prod"
export BEACON_S3_STORAGE_CLASS="STANDARD"
export BEACON_S3_WRITER="beacon-endpoint-writer"
Create the bucket. AWS requires a different command shape for us-east-1:
if [ "$AWS_REGION" = "us-east-1" ]; then
  aws s3api create-bucket \
    --bucket "$BEACON_S3_BUCKET" \
    --region "$AWS_REGION"
else
  aws s3api create-bucket \
    --bucket "$BEACON_S3_BUCKET" \
    --region "$AWS_REGION" \
    --create-bucket-configuration "LocationConstraint=$AWS_REGION"
fi
Create a dedicated writer and grant only s3:PutObject below the Beacon root prefix:
aws iam create-user --user-name "$BEACON_S3_WRITER"

aws iam put-user-policy \
  --user-name "$BEACON_S3_WRITER" \
  --policy-name BeaconEndpointWriteOnly \
  --policy-document "$(cat <<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:PutObject"],
    "Resource": "arn:aws:s3:::${BEACON_S3_BUCKET}/${BEACON_S3_PREFIX}/*"
  }]
}
EOF
)"
Create an access key:
aws iam create-access-key \
  --user-name "$BEACON_S3_WRITER" \
  --output json
AWS displays the secret only once. Store both values in your password manager or endpoint secret tooling, then export them without committing them or placing them in shell startup files:
export AWS_ACCESS_KEY_ID="<AccessKeyId from the previous command>"
export AWS_SECRET_ACCESS_KEY="<SecretAccessKey from the previous command>"
export AWS_SESSION_TOKEN="" # only set for temporary credentials
Then install only the S3 forwarder:
sudo env \
  BEACON_S3_BUCKET="$BEACON_S3_BUCKET" \
  BEACON_S3_PREFIX="$BEACON_S3_PREFIX" \
  BEACON_S3_STORAGE_CLASS="$BEACON_S3_STORAGE_CLASS" \
  AWS_REGION="$AWS_REGION" \
  AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
  AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
  AWS_SESSION_TOKEN="${AWS_SESSION_TOKEN:-}" \
  /opt/beacon/jamf/claude/s3/install-forwarder.sh
The helper stores the provider values in a root-owned 0600 file for the launchd service. For production, prefer temporary credentials, a managed profile, or web identity where your environment can refresh them. Configure bucket encryption, lifecycle, retention, and access logging in AWS rather than in Beacon.

Google Cloud Storage

You need a Google Cloud project where your current gcloud identity can enable APIs, create a bucket, create a service account, and manage bucket IAM. Confirm the active identity and project:
gcloud auth list --filter=status:ACTIVE
gcloud config get-value project
Choose the project, bucket, location, prefix, and writer name:
export GCP_PROJECT="<project-id>"
export GCP_LOCATION="us-west1"
export BEACON_GCS_BUCKET="<globally-unique-bucket>"
export BEACON_GCS_PREFIX="beacon-prod"
export BEACON_GCS_STORAGE_CLASS="STANDARD"
export BEACON_GCS_WRITER="beacon-endpoint-writer"
export BEACON_GCS_WRITER_EMAIL="${BEACON_GCS_WRITER}@${GCP_PROJECT}.iam.gserviceaccount.com"
Enable the APIs and create a uniform-access bucket:
gcloud services enable storage.googleapis.com iam.googleapis.com \
  --project "$GCP_PROJECT"

gcloud storage buckets create "gs://${BEACON_GCS_BUCKET}" \
  --project "$GCP_PROJECT" \
  --location "$GCP_LOCATION" \
  --default-storage-class "$BEACON_GCS_STORAGE_CLASS" \
  --uniform-bucket-level-access
Create the writer service account:
gcloud iam service-accounts create "$BEACON_GCS_WRITER" \
  --project "$GCP_PROJECT" \
  --display-name "Beacon endpoint GCS writer"
Grant it write-only access to the bucket:
gcloud storage buckets add-iam-policy-binding \
  "gs://${BEACON_GCS_BUCKET}" \
  --member "serviceAccount:${BEACON_GCS_WRITER_EMAIL}" \
  --role roles/storage.objectCreator
The packaged Vector 0.56 service on macOS needs a service-account JSON file. If your organization allows service-account keys, create one in a temporary directory:
credential_dir="$(mktemp -d)"
credential_file="${credential_dir}/beacon-gcs-writer.json"
umask 077

gcloud iam service-accounts keys create "$credential_file" \
  --project "$GCP_PROJECT" \
  --iam-account "$BEACON_GCS_WRITER_EMAIL"
Install the JSON outside Beacon-managed directories so upgrades and cleanup do not remove it:
export GOOGLE_APPLICATION_CREDENTIALS="/Library/Application Support/Your Organization/Secrets/beacon-gcs-writer.json"

sudo install -d -m 0700 -o root -g wheel \
  "$(dirname "$GOOGLE_APPLICATION_CREDENTIALS")"
sudo install -m 0600 -o root -g wheel \
  "$credential_file" \
  "$GOOGLE_APPLICATION_CREDENTIALS"

rm -rf "$credential_dir"
Then install only the GCS forwarder:
sudo env \
  BEACON_GCS_BUCKET="$BEACON_GCS_BUCKET" \
  BEACON_GCS_PREFIX="$BEACON_GCS_PREFIX" \
  BEACON_GCS_STORAGE_CLASS="$BEACON_GCS_STORAGE_CLASS" \
  GOOGLE_APPLICATION_CREDENTIALS="$GOOGLE_APPLICATION_CREDENTIALS" \
  /opt/beacon/jamf/claude/gcs/install-forwarder.sh
The endpoint writer intentionally cannot list or read objects. Validate with your current administrator identity or a separate identity with roles/storage.objectViewer. If service-account key creation is blocked by organization policy, stop here: packaged Vector 0.56 does not support external-account Workload Identity Federation on macOS. Configure lifecycle, retention, audit logging, and CMEK controls in Google Cloud rather than in Beacon.

Forward To Both

The services have separate labels, configs, credentials, and checkpoint directories. Install both helpers to forward the same local events to S3 and GCS. After exporting the provider variables from the two sections above, run:
sudo env \
  BEACON_S3_BUCKET="$BEACON_S3_BUCKET" \
  BEACON_S3_PREFIX="$BEACON_S3_PREFIX" \
  BEACON_S3_STORAGE_CLASS="$BEACON_S3_STORAGE_CLASS" \
  AWS_REGION="$AWS_REGION" \
  AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
  AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
  AWS_SESSION_TOKEN="${AWS_SESSION_TOKEN:-}" \
  /opt/beacon/jamf/claude/s3/install-forwarder.sh

sudo env \
  BEACON_GCS_BUCKET="$BEACON_GCS_BUCKET" \
  BEACON_GCS_PREFIX="$BEACON_GCS_PREFIX" \
  BEACON_GCS_STORAGE_CLASS="$BEACON_GCS_STORAGE_CLASS" \
  GOOGLE_APPLICATION_CREDENTIALS="$GOOGLE_APPLICATION_CREDENTIALS" \
  /opt/beacon/jamf/claude/gcs/install-forwarder.sh
This creates two independent services:
com.beacon.endpoint.s3-forwarder
com.beacon.endpoint.gcs-forwarder

4. Validate

Check the configured services:
sudo launchctl print system/com.beacon.endpoint.collector
sudo launchctl print system/com.beacon.endpoint.s3-forwarder  # when configured
sudo launchctl print system/com.beacon.endpoint.gcs-forwarder # when configured
Write destination and inventory validation events:
sudo /opt/beacon/bin/beacon endpoint s3 validate --system  # when configured
sudo /opt/beacon/bin/beacon endpoint gcs validate --system # when configured

sudo /opt/beacon/bin/beacon endpoint inventory heartbeat \
  --system \
  --force \
  --trigger manual \
  --trigger-harness manual \
  --working-dir /Users/Shared \
  --log-path /var/log/beacon-agent/runtime.jsonl
Vector batches for up to five minutes by default. Use an authorized reader to inspect the destination:
# AWS S3
aws s3 ls \
  "s3://${BEACON_S3_BUCKET}/${BEACON_S3_PREFIX}/runtime/" \
  --recursive \
  --region "$AWS_REGION"
aws s3 ls \
  "s3://${BEACON_S3_BUCKET}/${BEACON_S3_PREFIX}/inventory/" \
  --recursive \
  --region "$AWS_REGION"

# Google Cloud Storage
gcloud storage ls \
  "gs://${BEACON_GCS_BUCKET}/${BEACON_GCS_PREFIX}/runtime/**"
gcloud storage ls \
  "gs://${BEACON_GCS_BUCKET}/${BEACON_GCS_PREFIX}/inventory/**"
Inspect a runtime object and look for the destination validation event:
aws s3 cp \
  "s3://${BEACON_S3_BUCKET}/${BEACON_S3_PREFIX}/runtime/date=<YYYY-MM-DD>/<object>.jsonl.gz" \
  - \
  --region "$AWS_REGION" |
  gzip -dc | grep "Beacon endpoint S3 validation event"

gcloud storage cat \
  "gs://${BEACON_GCS_BUCKET}/${BEACON_GCS_PREFIX}/runtime/date=<YYYY-MM-DD>/<object>.jsonl.gz" |
  gzip -dc | grep "Beacon endpoint GCS validation event"
The endpoint writer identities are intentionally write-only. Run these inspection commands with your administrator identity or a separate reader. Generate a real event from every configured harness and confirm its harness.name appears in a remote runtime object.

5. Operate And Troubleshoot

The forwarders persist checkpoints, so restarts and package upgrades resume from the last observed offsets. Runtime starts at the end on first install to avoid unexpected historical backfill; inventory starts at the beginning so the first snapshot is not missed. Check Vector errors:
sudo tail -n 100 /tmp/com.beacon.endpoint.s3-forwarder.err
sudo tail -n 100 /tmp/com.beacon.endpoint.gcs-forwarder.err
Common causes of missing objects are:
  • The validation event was written before Vector started. Write another event.
  • The configured bucket, region, or root prefix does not match the IAM policy.
  • The service credential cannot create objects.
  • The GCS credential path is not absolute, root-owned, and mode 0400 or 0600.
  • The write-only endpoint identity is being used for a list/read validation command.

Rotate AWS credentials

Create a replacement key, rerun the S3 helper with the new values, confirm a new object, and only then delete the retired key:
aws iam create-access-key \
  --user-name "$BEACON_S3_WRITER" \
  --output json

export AWS_ACCESS_KEY_ID="<new AccessKeyId>"
export AWS_SECRET_ACCESS_KEY="<new SecretAccessKey>"

sudo env \
  BEACON_S3_BUCKET="$BEACON_S3_BUCKET" \
  BEACON_S3_PREFIX="$BEACON_S3_PREFIX" \
  BEACON_S3_STORAGE_CLASS="$BEACON_S3_STORAGE_CLASS" \
  AWS_REGION="$AWS_REGION" \
  AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
  AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
  /opt/beacon/jamf/claude/s3/install-forwarder.sh

aws iam delete-access-key \
  --user-name "$BEACON_S3_WRITER" \
  --access-key-id "<retired-access-key-id>"

Rotate Google credentials

Create a replacement key in a temporary directory, atomically replace the externally managed file, restart Vector, confirm a new object, and then delete the retired service-account key:
next_dir="$(mktemp -d)"
next_key="${next_dir}/beacon-gcs-writer.json"
umask 077

gcloud iam service-accounts keys create "$next_key" \
  --project "$GCP_PROJECT" \
  --iam-account "$BEACON_GCS_WRITER_EMAIL"

sudo install -m 0600 -o root -g wheel \
  "$next_key" \
  "${GOOGLE_APPLICATION_CREDENTIALS}.new"
sudo mv -f \
  "${GOOGLE_APPLICATION_CREDENTIALS}.new" \
  "$GOOGLE_APPLICATION_CREDENTIALS"
rm -rf "$next_dir"

sudo launchctl kickstart -k system/com.beacon.endpoint.gcs-forwarder

gcloud iam service-accounts keys list \
  --project "$GCP_PROJECT" \
  --iam-account "$BEACON_GCS_WRITER_EMAIL"
gcloud iam service-accounts keys delete "<retired-key-id>" \
  --project "$GCP_PROJECT" \
  --iam-account "$BEACON_GCS_WRITER_EMAIL"
For managed fleets, use the dedicated Jamf S3 guide or Jamf GCS guide. Those guides show the optional Claude-oriented combined repair wrappers as well as policy parameters and managed credential delivery.