# TinyActivate: backend-confirmed onboarding

TinyActivate displays 1–5 onboarding milestones and remembers progress across
browser sessions. It is a sidecar: it never controls your application's access,
billing, entitlements or critical business actions.

## Agent setup

Download the current CLI and verify its checksum using https://api.tinyscale.io/agent.md.
In a new project directory, opt in explicitly:

    bun ./tinyscale-agent-v0.2.0.mjs workspace create --name "My project" --products tinyactivate
    bun ./tinyscale-agent-v0.2.0.mjs activate create --configuration-json '{"name":"Getting started","allowedOrigins":["https://your-owned-app.example"],"steps":[{"key":"project_created","label":"Create a project","description":"Start your first project.","actionPath":"/projects/new"}]}'
    bun ./tinyscale-agent-v0.2.0.mjs activate list
    bun ./tinyscale-agent-v0.2.0.mjs activate test --checklist CHECKLIST_ID --event project_created
    bun ./tinyscale-agent-v0.2.0.mjs activate status --checklist CHECKLIST_ID --revision 1 --cohort test

The test command uses one fixed synthetic subject for this checklist. It does
not impersonate a customer. To revise, use activate revise with
--checklist, --expected-revision and --configuration-json. Existing enrollments
keep their original revision; new subjects use the current revision. Removing
an allowed origin immediately blocks that origin's old sessions too.

The CLI stores credentials privately in .tinyscale. Do not read this directory
into model context, logs or source control. Existing projects created without
TinyActivate keep their original permissions; this release does not silently
upgrade their keys. Use a separate explicitly provisioned project for a new trial.

## Customer backend integration

Use the project's credential only on your backend. Treat deployment secrets as
opaque: a human or approved secret-management flow must install the credential.
Never paste it into a model, browser bundle, command argument, URL or log.

The repository's @tinyscale/agent-client package exposes createActivateChecklist,
reviseActivateChecklist, recordActivateEvent, createActivateSession and
activateSummary. It is not a published npm package. External applications can
use the following versioned HTTP endpoints directly with a Bearer credential:

- GET /v1/agent/tinyactivate: installation ID and usage.
- GET/POST /v1/installations/{id}/tinyactivate/checklists: list/create.
- POST /v1/installations/{id}/tinyactivate/checklists/{checklist}: revise with
  expectedRevision and configuration.
- POST /v1/installations/{id}/tinyactivate/checklists/{checklist}/events: milestone.
- POST /v1/installations/{id}/tinyactivate/checklists/{checklist}/sessions: read session.
- GET /v1/installations/{id}/tinyactivate/checklists/{checklist}/summary?revision=1&cohort=test.

All request bodies use schemaVersion "2026-09-07". Configure/revise requests
contain idempotencyKey and configuration (plus expectedRevision when revising).
The event body is exactly:

    {"schemaVersion":"2026-09-07","idempotencyKey":"YOUR_STABLE_OUTBOX_EVENT_ID","subjectDigest":"64_LOWERCASE_HEX_CHARACTERS","cohort":"live","eventKey":"project_created"}

Generate subjectDigest with HMAC-SHA256(customer-backend secret, your internal
user ID), encoded as lowercase hex. Never send email addresses, names, raw IDs,
form values, cookies, properties or arbitrary metadata. Keep the HMAC secret
stable; changing it creates new enrollments and consumes lifetime allowance.
Use cohort test for synthetic testing; live is available only after human claim.

Write a small durable outbox intent in the same transaction as the customer
business action. A background sender delivers it after commit. Use that outbox
record's stable ID as idempotencyKey on every retry. Retry transport/503 failures
with bounded backoff. Do not retry permanent 400/403/409/410 responses unchanged.
On 429, inspect limits; Preview/Free never incur overage. A failed TinyActivate
delivery must not undo or block the customer's completed business action.

Accepted events return eventId, outcome, completedSteps, totalSteps, complete
and receivedAt. Reordered events are supported. Browser clicks cannot mark a
milestone complete. Reusing a key with changed input conflicts. Up to ten
alternate retry keys are remembered for an already completed milestone;
always reuse the original key rather than generating a new one on each retry.

## Browser widget

Implement POST /api/onboarding/session on your own origin. Authenticate the
current application user there; derive their digest server-side. Ignore any
caller-supplied subject. Enforce your usual same-origin/CSRF policy. Send:

    {"schemaVersion":"2026-09-07","subjectDigest":"64_LOWERCASE_HEX_CHARACTERS","cohort":"live","origin":"https://your-owned-app.example"}

to the sessions endpoint. Return its JSON response only to that authenticated
user, with Cache-Control: no-store. The response contains schemaVersion, token,
expiresAt and progressUrl. Do not log it. Tokens are read-only, origin-bound,
15-minute credentials, with at most five unexpired sessions per enrollment.

After your human-approved publication, embed:

    <div id="onboarding"></div>
    <script src="https://ingest.tinyscale.io/v1/activate/widget.v1.js" defer></script>
    <script src="/onboarding.js" defer></script>

In /onboarding.js:

    const widget = TinyActivate.mount(document.getElementById('onboarding'), {
      sessionPath: '/api/onboarding/session'
    });

Call widget.refresh() after the backend finishes a milestone and widget.destroy()
when removing the view. Tokens remain in memory. The widget uses a shadow root,
plain text and same-origin action links. It polls every 30 seconds while visible,
with at most 30 refresh attempts per mount and eight-second request deadlines.
It stops polling when complete and fails independently of the host app.
Allow the ingest origin in script-src and connect-src in your CSP. The widget
includes scoped styles, so your style-src policy must allow those styles.

## Limits, claim, privacy and owner controls

Preview lasts 72 hours and accepts synthetic subjects only: two checklists,
25 lifetime enrollments and 200 accepted events. Human claim preserves IDs and
existing integrations on Free: five lifetime checklists, 1,000 lifetime
enrollments and 2,000 accepted events per UTC month. There are at most 20
revisions per checklist and five milestones per enrollment. Deleting subject
data does not refund consumed lifetime allowance. Session issuance is bounded;
repeated full-page reloads can reach the five-session limit until sessions expire.

After proving the integration, run workspace claim. The human signs in and
claims the project on Free; no payment is taken. The project view at
https://app.tinyscale.io/agent-workspaces shows revision/cohort counts, limits,
pause/resume controls, aggregate/configuration export and subject erasure.
The internal TinyScale setup checklist uses one checklist and one synthetic
enrollment to exercise the same service. Those counts are explicitly test data.

Pause stops new events, sessions and public progress reads. Disconnect stops
project collection and agent access. Records are retained until explicit erasure;
this release has no automatic deletion schedule. The human owner can erase a
subject by checklist, digest and cohort, including after disconnect. Stop sending
that subject's events first: a later event can create a new enrollment. Erasure
removes enrollment, events, retry keys and sessions. Configuration and aggregate
usage counters remain. Export contains configuration and aggregate results, not
subject-level data. No subject lookup or real-user/session tool is exposed to models.

Incomplete milestone counts are observations, not proof of churn or conversion lift.
The owned synthetic demo is https://fixtures-staging.tinyscale.io/activate/production.
