Skip to content

Public mirror

This page is a redacted public mirror of an internal aster-cloud document. For executed copies, customer-specific terms, or unredacted internal references, contact hello@aster-lang.dev.

The customer-facing Data Subject Access Request endpoint lets your deployment exercise GDPR Art 15 (access) and Art 17 (erasure) against the telemetry rows we hold on the Aster SaaS side — without filing a support ticket or logging in to a SaaS dashboard.

Endpoint: POST https://aster-lang.cloud/api/v1/dsar

Authentication: per-license HMAC-SHA256 (the same secret you already use for the telemetry uploader). If you have a working /api/cron/telemetry-uploader setup, you already have the credentials you need.

Request shape

Body (HMAC-signed canonical JSON):

jsonc
{
  "action": "access" | "delete",
  "subject": "license" | "customer",
  "dryRun": false,            // optional; default false
  "dsarRef": "DSAR-2026-0517",// required; recorded in the audit row
  "nonce": "<16+ chars>",
  "timestamp": "2026-05-19T12:00:00.000Z"
}
FieldRequiredNotes
actionyesaccess returns rows; delete purges.
subjectyeslicense scopes to this license only; customer scopes to all licenses owned by the same customer (cross-checked against the license's customer field).
dryRunnoWhen true, no rows are deleted; the response shows the count that would be deleted. Recorded in the audit log as dry-run-preview.
dsarRefyesYour internal ticket number; we record it so regulators can match Aster's audit to your own.
nonceyesAnti-replay random bytes (≥ 16 chars).
timestampyesISO-8601; rejected if more than ±5 minutes from server time.

Headers (identical to ingest):

x-aster-license-id:    lic_...
x-aster-customer:      Acme Corp   # or anon-<hex>-<len> when ASTER_TELEMETRY_MASK_CUSTOMER=1
x-aster-deployment-id: <64-hex>    # optional but recommended
x-aster-signature-kid: default
x-aster-signature-alg: HMAC-SHA256
x-aster-signature:     <base64url HMAC over the body>

curl examples

sh
# 1) Preview a delete (dry-run)
SECRET="$ASTER_TELEMETRY_SECRET"
LIC="lic_abc123"
DEP="aaaaaaaa...aaaaaaaa"   # 64 hex
NOW="$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
NONCE="$(openssl rand -hex 16)"
BODY=$(jq -c -n --arg ref "DSAR-PREVIEW" --arg ts "$NOW" --arg n "$NONCE" '
  { action:"delete", subject:"license", dryRun:true, dsarRef:$ref, nonce:$n, timestamp:$ts }')
SIG=$(printf '%s' "$BODY" | openssl dgst -binary -sha256 -hmac "$SECRET" \
        | base64 | tr '+/' '-_' | tr -d '=')

curl -sS -X POST https://aster-lang.cloud/api/v1/dsar \
  -H "content-type: application/json" \
  -H "x-aster-license-id: $LIC" \
  -H "x-aster-customer: Acme Corp" \
  -H "x-aster-deployment-id: $DEP" \
  -H "x-aster-signature-kid: default" \
  -H "x-aster-signature-alg: HMAC-SHA256" \
  -H "x-aster-signature: $SIG" \
  -d "$BODY"
# → { "action":"delete", "subject":"license", "licenseId":"lic_abc123", "rowsDeleted": 38, "dryRun": true }
sh
# 2) Apply the deletion after reviewing the preview
BODY=$(jq -c -n --arg ref "DSAR-2026-0517" --arg ts "$NOW" --arg n "$NONCE" '
  { action:"delete", subject:"license", dryRun:false, dsarRef:$ref, nonce:$n, timestamp:$ts }')
SIG=$(printf '%s' "$BODY" | openssl dgst -binary -sha256 -hmac "$SECRET" \
        | base64 | tr '+/' '-_' | tr -d '=')

curl -sS -X POST https://aster-lang.cloud/api/v1/dsar \
  -H "content-type: application/json" \
  -H "x-aster-license-id: $LIC" \
  -H "x-aster-customer: Acme Corp" \
  -H "x-aster-deployment-id: $DEP" \
  -H "x-aster-signature-kid: default" \
  -H "x-aster-signature-alg: HMAC-SHA256" \
  -H "x-aster-signature: $SIG" \
  -d "$BODY"
# → { "action":"delete", ..., "rowsDeleted": 38, "dryRun": false }
sh
# 3) Access request (GDPR Art 15) — receive every row Aster holds
BODY=$(jq -c -n --arg ref "DSAR-ACCESS-001" --arg ts "$NOW" --arg n "$NONCE" '
  { action:"access", subject:"customer", dsarRef:$ref, nonce:$n, timestamp:$ts }')
# ... sign + POST as above ...
# → { "action":"access", "rows":[ ...full LicenseTelemetry rows... ], "retainedFor90DaysAuditOnly": true }

Error responses

All authentication failures return the same { "error": "rejected" } shape so an attacker cannot probe which licenses exist:

HTTPreasonMeaning
400signature-verify-failedWrong secret / wrong license id / wrong kid.
400customer-mismatchx-aster-customer header doesn't match the license's customer.
400deployment-id-mismatchLicense is bound to a different deployment.
400invalid-action / invalid-subjectBody shape error.
400stale-timestampMore than 5 min skew.
400invalid-nonceMissing or <16 chars.
400dsarRef-requiredMissing audit-trail reference.
404(empty body)Endpoint hit on the on-prem build. SaaS only.

Audit trail

Every request — including dryRun previews — writes a row to the TelemetryAccessAudit table on the SaaS side:

  • Action: read-list for access, delete-by-dsar for committed deletes, dry-run-preview for previews.
  • Actor: customer-dsar:<licenseId> so the source of the request is unambiguous in regulator-facing reports.
  • Metadata: dsarRef, row count, optional request id.

Delete-audit rows are retained 7 years (legal hold); read and dry-run audits 90 days. See telemetry overview for the retention contract.

  • Telemetry overview: the telemetry overview (internal)
  • Per-field GDPR justification: telemetry fields
  • DPA template (Art 28): DPA template
  • Privacy notice (live, customer-facing): <saas-host>/<locale>/privacy

Released under the Apache License 2.0.