tinkaria
Artifacts

Create an artifact

Create a live dist from a curated template or compact Emmet source.

This path starts from a long bearer key, creates the app descriptor in your namespace, then publishes a live dist. POST /apps never accepts a caller-chosen namespace; it forces the descriptor namespace to the bearer principal and returns 409 if the app already exists.

Use a short key scoped to the app and the REST uses you need:

# docjourney: step=short-key
curl -s -X POST https://t.tini.works/token/short \
  -H "authorization: Bearer <long-key>" \
  -H "content-type: application/json" \
  -d '{"ttlSeconds":900,"scope":{"uses":["author_app","publish_content","await_app","input_app","render_app"],"app_ids":["journey"]}}'
# -> {"token":"<short-key>","issuedAt":"...","expiresAt":"...","ttlSeconds":900}

# docjourney: step=create-app
curl -s -X POST https://t.tini.works/apps \
  -H "authorization: Bearer <short-key>" \
  -H "content-type: application/json" \
  -d '{"appId":"journey","authMode":"anonymous"}'
# -> {"ok":true,"namespace":"<your-handle>","appId":"journey","authMode":"anonymous"}

For the canonical curl-only journey and patch-vs-replace rule, see docs/AUTHORING.md section 14.4.

Pick a create mode

ModeUse it whenRequest
TemplateYou want a known safe page shape with typed slots.POST /apps/{id}/content?target=dist&mode=template
EmmetYou want token-efficient HTML from compact source.POST /apps/{id}/content?target=dist&mode=emmet

Both modes require target=dist. Success computes the artifact sha256, stores the artifact, and updates promotion.distRef in the same request.

Templates

Curated templates are typed and escaped by default. Missing, unknown, or mistyped values fail closed. URL slots must be absolute allowed URLs. The rendered output is rejected if it contains active content such as script tags, event handler attributes, unsafe URL schemes, or CSS active tokens.

TemplateSlot types
curated:contact-form-v1text: body_class, page_title, form_title, intro_text, submit_label, privacy_note; URL: action_url; lists: fields, helper_points
curated:ops-dashboard-v1text: body_class, app_title, owner_label, status_text, page_title, primary_metric, secondary_metric; URL: docs_url; lists: alerts, actions
# Create a dist artifact from a curated template and update promotion.distRef.
# docjourney: step=template-create
curl -s -X POST "https://t.tini.works/apps/journey/content?target=dist&mode=template" \
  -H "authorization: Bearer <short-key>" \
  -H "content-type: application/json" \
  -d '{"template_id":"curated:contact-form-v1","values":{"body_class":"form-page","page_title":"Doc Journey","form_title":"Contact PKR Docs","intro_text":"Patch this page through the artifact edge.","action_url":"https://example.com/contact","fields":["Name","Email"],"helper_points":["Escaped by default","No local packaging step"],"submit_label":"Send","privacy_note":"No spam."}}'
# -> {"artifact_id":"<sha256>","app":"journey","live":true,"hash":"<sha256>","target":"dist","mode":"template","template_id":"curated:contact-form-v1","template_hash":"<sha256>","manifest_hash":"<sha256>","values_hash":"<sha256>","expanded_hash":"<sha256>","engine_version":"template-v1","active_content":false}
curl -s -X POST "https://t.tini.works/apps/journey/content?target=dist&mode=template" \
  -H "authorization: Bearer <short-key>" \
  -H "content-type: application/json" \
  -d '{"template_id":"curated:ops-dashboard-v1","values":{"body_class":"ops-dashboard","app_title":"Ops","owner_label":"SRE","status_text":"Ready","docs_url":"https://example.com/docs","page_title":"Live status","primary_metric":"99.99%","secondary_metric":"42 ms","alerts":["Queue < 5"],"actions":["Check deploy"]}}'
# -> {"artifact_id":"<sha256>","app":"journey","live":true,"hash":"<sha256>","target":"dist","mode":"template","template_id":"curated:ops-dashboard-v1","template_hash":"<sha256>","manifest_hash":"<sha256>","values_hash":"<sha256>","expanded_hash":"<sha256>","engine_version":"template-v1","active_content":false}

Emmet

Emmet mode accepts plain text Emmet source or a JSON envelope {emmet, title, css}. It is a deterministic safe subset for compact HTML, not arbitrary browser code.

Rejected fail-closed: unsupported syntax, script, link, meta http-equiv, on* attributes, javascript: or data: URL schemes, and remote CSS via @import or url(http...). Unsafe CSS in the css envelope is rejected too.

# Create a second dist artifact from an Emmet source or JSON envelope.
# docjourney: step=emmet-create
curl -s -X POST "https://t.tini.works/apps/journey/content?target=dist&mode=emmet" \
  -H "authorization: Bearer <short-key>" \
  -H "content-type: application/json" \
  -d '{"emmet":"main#emmet-proof>(h1{Emmet proof}+button.cta[data-action=select]{Select})","title":"Emmet proof","css":"body{font-family:sans-serif}.cta{padding:8px}"}'
# -> {"artifact_id":"<sha256>","app":"journey","live":true,"hash":"<sha256>","target":"dist","mode":"emmet","source_hash":"<sha256>","expanded_hash":"<sha256>"}

Template and Emmet are exchangeable. Create with compact Emmet when the first draft is small, then patch index.html later. Create from a curated template when the page shape matters more than source compactness, then patch only the changed anchor.

On this page