One build, fourteen apps: see what users made of Memory Tin

Documentation

AI

Documentation menu

In edit mode the tray gets a "Describe the change" field. The user types a request in their own language. The hosted backend asks the model for a change, validates the answer against your catalog and locks, and returns a proposal. The SDK draws it as a preview with a list of what changes; the user taps Apply or Discard. Apply is validated again on the device, saved, synced and recorded as one undo step.

Route Edits
POST /ai/patch one screen: layout, theme, options, style; on a user screen it may place portable parts and generic pieces
POST /ai/navigation the navigation document: entries, menus, start screen
POST /ai/designs named designs: create, switch, rename, duplicate, delete, restore, undo

The model is Claude by Anthropic.

Turn it on

AI needs sync. It is off for an app until you switch it on in the dashboard, and off in the app until you configure it in the SDK:

let engine = TweakLaneEngine(
    catalog: makeCatalog(), userId: account.id, sync: syncConfiguration,
    ai: AITweakConfiguration(providerName: "Anthropic",
                             privacyPolicyURL: URL(string: "https://example.com/privacy")))
engine.ai?.isEnabled = false   // host switch at any time; nothing is sent while off

A kill switch also turns AI off.

Before the first request leaves the device, a sheet names the provider, says what is sent and asks for permission. App Review guideline 5.1.2(i) requires disclosing that personal data is shared with a third-party AI and obtaining explicit permission. Consent is stored per user on the device and on the server, and the server refuses prompts without it (HTTP 403 consent_required). It is tied to the provider name and a consent version: change either and users are asked again. Users can turn AI off at any time; consent is revoked on the device at once and on the server when reachable.

Route Purpose
GET /ai/consent {enabled, provider, consent}
PUT /ai/consent body {provider, consentVersion}; records consent
DELETE /ai/consent revokes consent

What is sent to the model

The server, never the app, calls the model. It sends an instruction text, your catalog's component ids, display names, lock levels and alternatives, the locked node ids of the screen, the screen's current layout (node ids, component ids, order, visibility, size, alignment, props and labels) and the user's request. No user id, token, device data or other screens. Props are part of the layout, so keep personal data out of props.

How answers are checked

The model's output is treated as untrusted. It passes, in order:

  1. a size cap (32 KB by default)
  2. a JSON object with operations (at most 50) or a full document; unknown fields are dropped
  3. no URL-like strings that are not already in the layout or catalog
  4. operations applied with the same rules as manual edits
  5. AI policy: no new components on host screens, none removed, swaps only within alternatives, props only to values the app already uses, labels and lock fields reset to your own
  6. the full server validator: catalog, locks, structure, limits
  7. it must change something

Any failure returns {status: "rejected", reasons} and nothing is applied. Locks can't be bypassed by a prompt: a request to hide the prices or drop Restore Purchases comes back rejected or as a safe substitute with a note.

Limits and retention

Setting Default
Requests per user 5 per minute, 30 per hour (HTTP 429 rate_limited)
Timeout 20 s
Prompt length 500 characters
Answer size 32 KB
Store prompts (ai_store_prompts) on; off stores only the prompt length
Prompt log retention 30 days

Each AI request writes one log row per app: user id, screen, provider, model, catalog hash, the prompt (unless switched off), outcome, reason codes, output size and latency. Rows older than the retention period are deleted daily. Consent records are kept until the app is deleted, as the record of consent.