Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,29 @@ jobs:
- name: Run integration tests
run: npm run test:integration

agent365-storage:
name: Agent365 storage (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run Agent365 filesystem and exporter regressions
run: npm run test:unit -- test/internal/unit/a365/persistentStore.test.ts test/internal/unit/a365/durableDeliveryManager.test.ts test/internal/unit/a365/agent365Exporter.test.ts test/internal/unit/a365/windowsFileAccessControl.test.ts
- name: Verify cross-process persistence and replay
run: node --test test/integration/durable-storage.test.mjs

changelog:
name: CHANGELOG updated
runs-on: ubuntu-latest
Expand Down
68 changes: 49 additions & 19 deletions A365_DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,9 @@ When both `tokenResolver` and `contextualTokenResolver` are set, `contextualToke

## Durable Delivery

Durable delivery is enabled by default for the A365 HTTP exporter when retryable A365 HTTP exports
must survive process restarts.
Durable delivery is requested by default for the A365 HTTP exporter. **On Unix, including Linux
and macOS, it requires an explicitly configured, existing, absolute storage root.** Missing or
invalid configuration logs an error and disables only offline buffering/replay; online export continues.

```typescript
import { useMicrosoftOpenTelemetry } from "@microsoft/opentelemetry";
Expand All @@ -282,16 +283,16 @@ useMicrosoftOpenTelemetry({
});
```

Durable delivery is enabled by default. Set `durableDelivery.enabled: false` to force legacy
Durable delivery is requested by default. Set `durableDelivery.enabled: false` to force legacy
network-only delivery. It applies only to the A365 HTTP exporter, so set
`enableObservabilityExporter: true` alongside `a365.enabled: true`.

### Durable Delivery Defaults

| Option | Default | Notes |
| ------------------------------------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled` | `true` | Durable delivery stays on unless you explicitly disable it |
| `storageDirectory` | auto | Uses the configured directory, or creates a secure platform-specific default root plus a stable per-application `app-<hash>` partition |
| `enabled` | `true` | Requests offline buffering/replay; `false` bypasses storage initialization entirely |
| `storageDirectory` | unset | Unix: required existing absolute customer-provisioned root. Windows: existing platform defaults. Stable `app-<hash>` cache partition |
| `maxStorageBytes` | `50 * 1024 * 1024` | Bounds pending, quarantined, active leased, and non-stale temporary records within the current `app-<hash>` partition only |
| `maxRecordAgeMilliseconds` | `2 * 24 * 60 * 60 * 1000` | Expired records are pruned before capacity eviction, within the current `app-<hash>` partition only |
| `replayIntervalMilliseconds` | `2 * 60 * 1000` | Scheduled replay cadence |
Expand All @@ -302,15 +303,19 @@ network-only delivery. It applies only to the A365 HTTP exporter, so set

### Operational Notes

- Durable records are stored as plaintext JSON files. On POSIX, the SDK creates owner-only durable
directories/files (`0700` / `0600`), rejects symlink roots, and requires the root to be owned by
the current user. Default POSIX storage probes `TMPDIR`, `/var/tmp`, then `/tmp` and atomically
creates one `a365-otel-durable-<uid>` leaf under each candidate; it does not create a
multi-directory SDK-owned tree under a shared temp directory. Windows defaults use the per-user
`Microsoft/A365/otel-durable` location under the selected candidate and also reject a symlinked
final root. On Windows, the SDK removes inherited ACLs and grants full control only to the current
Windows identity and built-in Administrators. ACL hardening failures cause durable storage
initialization to fail and the exporter to use network-only delivery.
- Durable records are plaintext JSON. On Unix the configured root is a customer-trusted security
boundary. The SDK checks configuration and directory existence, not confidentiality: it does not
inspect or rewrite ownership, permissions, ACLs, symlinks, or ancestors. Customers protect these
paths throughout the application's lifetime. Configuring `/tmp` explicitly is still unsafe.
- Unix never selects `TMPDIR`, `/var/tmp`, `/tmp`, or the current directory implicitly and never
creates the configured root. It creates only the normal `app-<hash>` cache beneath a valid root.
Existing file/directory creation modes (`0600`/`0700`, filtered by umask) are retained without
chmod/chown or permission repair. Customers must use a compatible service umask; the SDK does not
override it. Existing customer root/cache permissions are unchanged.
- Windows retains its existing `LOCALAPPDATA`, `TEMP`, and `os.tmpdir()` candidate behavior,
`Microsoft/A365/otel-durable` layout, final-root symlink rejection, and ACL helper. Inherited ACLs
are removed and full control granted to the current identity and Administrators as before this
issue. ACL initialization failures are logged and disable only offline buffering/replay.
- Every explicit or default durable root is partitioned again under a stable `app-<hash>` child
derived from the process identity, so applications that share a base directory do not replay one
another's telemetry. All retention, capacity, and pruning behavior described below operates only
Expand All @@ -319,11 +324,12 @@ network-only delivery. It applies only to the A365 HTTP exporter, so set
executable upgrade), the previous partition becomes orphaned and is not age-pruned, capacity-
pruned, or otherwise cleaned up automatically. Operators who need to reclaim that space should
remove stale `app-<hash>` directories under the durable root out-of-band.
- Durable delivery is enabled by default when the A365 HTTP exporter is active. Set
- Durable delivery is requested by default when the A365 HTTP exporter is active. Set
`durableDelivery.enabled: false` to force legacy network-only delivery.
- If durable storage initialization fails, the exporter logs the error and continues in network-only
mode. Successful and non-retryable live sends still complete, but retryable responses that cannot
be persisted are reported as failures.
mode. Successful online sends still succeed; failed network sends that cannot be persisted are
reported as failures. Initialization is shared by startup, export, flush, and shutdown paths;
missing/invalid configuration does not start replay or trigger a hidden storage fallback.
- Delivery is at-least-once. A retryable request can be replayed after a crash, timeout, or
shutdown race, so downstream consumers must tolerate duplicates.
- HTTP 401, 408, 429, and 5xx responses plus transport failures are retryable. Live delivery
Expand All @@ -342,10 +348,34 @@ network-only delivery. It applies only to the A365 HTTP exporter, so set
retryable response pauses both immediate sends and replay probes until the gate reopens. The
effective delay is capped at one hour.
- If records must survive container restarts, rescheduling, or host restarts, point
`storageDirectory` at a protected persistent volume. Default temp directories and container
filesystems are convenient for process restarts, but ephemeral storage can be lost when a
`storageDirectory` at a protected persistent volume. Ephemeral storage can be lost when a
container is replaced and still counts against container ephemeral-storage limits.

### Storage provisioning and migration

**Unix breaking change:** applications that previously relied on implicit temporary storage must
provision and configure a root or set `durableDelivery.enabled: false`. During deployment, create a
dedicated private directory as the application's service account, normally mode `0700`, for example:

```sh
install -d -m 700 "$HOME/.local/state/my-agent/telemetry"
```

Review existing ownership, extended/inherited ACL grants, symlinks, and ancestor protection before
reuse. Do not chmod shared system directories. Configure the absolute path through
`a365.durableDelivery.storageDirectory` (or the exporter's `durableDelivery.storageDirectory`);
the example's `A365_DURABLE_STORAGE_DIRECTORY` is application-read configuration, not an automatic
SDK environment override. Root validation is not a security audit.

Reusing the same explicit root, service identity, executable path, and application entry point
preserves `app-<hash>` partition identity and existing record replay. Retention limits and the record
format are unchanged. No old temporary-directory backlog is discovered or migrated automatically.
For a controlled migration, stop all writers/replayers, review the old cache and provenance, secure
the destination root and ACLs, and move the existing application partition intact. If executable or
application identity changes, its partition hash also changes; review the intended destination
partition before transferring records. Resume one instance first and confirm replay, allowing for
at-least-once duplicates. Retain a protected backup until migration is verified.

## Shutdown

Call `shutdownMicrosoftOpenTelemetry()` during graceful shutdown to flush pending telemetry and release resources:
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## [Unreleased]

### Bugs Fixed
- **Breaking change on Unix (Linux/macOS):** Agent365 offline buffering/replay now requires an explicitly configured, existing, absolute `durableDelivery.storageDirectory`. No automatic Unix temporary-directory fallback or root creation. Missing/invalid roots are logged and disable only disk buffering/replay; online export continues. Customers provision and maintain ownership, permissions (normally a private service-account-owned `0700` root), ACLs, and path-replacement protection; the SDK does not inspect or repair Unix access controls. Windows behavior, explicit-root cache identities, record compatibility, and retention limits are unchanged. Old temporary backlogs are not automatically migrated; see [storage provisioning and migration](A365_DOCUMENTATION.md#storage-provisioning-and-migration).

### Features Added
- Add privacy-safe Model Observation attributes to `InferenceScope`, including the canonical served response model and cache read/write input-token counts. Attributes can be supplied when the scope starts or recorded after the response is available; inference operations accept enum values or canonical strings and use canonical OpenTelemetry names for both operation attributes and span names. Cache-token semantic-convention constants are exported. [#261](https://github.com/microsoft/opentelemetry-distro-javascript/pull/261)

Expand Down
Loading
Loading