Referenz

samy.js, samy.css, Session-Token

Alles für die Seite deiner App: die Brücke, das Stylesheet und das Token.

Als Markdown öffnen

Zum Nachschlagen für die Seite deiner App: die Brücke samy.js, das Session-Token und das Stylesheet samy.css.

samy.js

html
<script src="https://packlets.app/embed/samy.js"></script>

Danach gibt es das Objekt samy:

AufrufErgebnis
samy.inSamytrue, wenn die Seite in der Samy-App läuft, sonst false. Kein Aufruf, sondern ein Wert.
samy.launchStartdaten, sofort beim Laden da: { v, packlet_id, platform, theme }. Kein Aufruf, sondern ein Wert.
samy.themeSamys Farben (siehe Farben). Kein Aufruf, sondern ein Wert.
samy.ready()Meldet, dass die Seite fertig ist. Samy blendet dann den Platzhalter aus. Nur nötig mit data-ready="manual".
await samy.token()Das Session-Token als Text. Direkt nach dem Laden kommt es ohne Wartezeit aus den Startdaten.
await samy.fetch(url, init)Wie fetch(), setzt aber Authorization: Bearer <Token> mit einem frischen Token. Gibt die normale Antwort zurück.
await samy.info(){ app: "samy", version: 2, packlet_id: "…", platform: "iOS", theme: {…} }. platform ist iOS oder android.
samy.toast("Gespeichert")Kurze Meldung in der App. Der Text wird nach 140 Zeichen abgeschnitten.
samy.open("https://…")Öffnet die Adresse im Browser des Handys. Nur http(s).
samy.haptic("success")Vibration: tap (Standard), selection, success oder error.
await samy.storage.get(key)Gespeicherter Text oder null. Siehe Speicher.
await samy.storage.set(key, text)Speichert Text (höchstens 16 KB).
await samy.storage.remove(key)Löscht einen Eintrag.
await samy.storage.keys()Alle Schlüssel dieser App.

Alle Aufrufe außer samy.inSamy geben ein Promise zurück.

  • Außerhalb von Samy (zum Beispiel im Browser) schlagen die Aufrufe sofort fehl. Frag deshalb zuerst samy.inSamy ab.
  • Zeitlimit: Antwortet die App nicht innerhalb von 15 Sekunden, schlägt der Aufruf fehl.
  • Nur deine Adresse: Antworten bekommen nur Seiten mit derselben Herkunft (https, Host und Port) wie die Adresse, die du als page gesetzt hast.
  • Nur die Hauptseite: Jeder Aufruf trägt einen geheimen Brücken-Schlüssel aus den Startdaten. Aufrufe aus iframes fremder Adressen haben ihn nicht, Samy ignoriert sie. Mehr unter iframes und fremde Inhalte.
  • Läuft die Seite in Samy, trägt <html> die Klasse in-samy. Ist das Skript geladen, feuert auf window das Ereignis samy:ready.
  • Wieder geöffnet: Samy hält die zuletzt geöffneten Seiten im Speicher (bis zu vier). Kommt jemand zurück, lädt die Seite nicht neu. Stattdessen feuert auf window das Ereignis samy:activated. Das ist der richtige Moment, veraltete Daten aufzufrischen. Der Knopf oben rechts in der App lädt die Seite bei Bedarf ganz neu.

Laden und Platzhalter

Bis deine Seite fertig ist, zeigt Samy Avatar und Name der App. Normalerweise verschwindet der Platzhalter, sobald die Seite geladen ist. Lädt deine Seite danach noch Daten nach, bindest du das Skript mit data-ready="manual" ein und rufst selbst samy.ready(), wenn alles steht. Kommt nach sechs Sekunden kein ready(), blendet Samy den Platzhalter trotzdem aus.

html
<script src="https://packlets.app/embed/samy.js" data-ready="manual"></script>
<script>
  loadEverything().then(() => samy.ready());
</script>

Startdaten

Samy hängt beim Laden Startdaten an die Adresse, hinter #samy= (Base64url-kodiertes JSON). Der Teil hinter # geht nie an deinen Server. samy.js liest ihn sofort aus, stellt ihn als samy.launch bereit und entfernt ihn wieder aus der Adresse. Das Session-Token aus den Startdaten liefert samy.token() in den ersten 45 Sekunden ohne Umweg über die App. So weiß deine Seite schon beim ersten Aufruf deines Servers, wer sie öffnet (wie initData bei Telegram).

Den Brücken-Schlüssel aus den Startdaten legt samy.js im sessionStorage deiner Adresse ab. So funktioniert samy auch auf weiteren Seiten, zu denen du innerhalb deiner Adresse verlinkst. Du musst dafür nichts tun.

Farben

samy.theme enthält Samys Farben: bg, surface, text, text_secondary, text_tertiary, field, cta, on_cta, danger und scheme (light). samy.js setzt sie zusätzlich als CSS-Variablen auf <html>, zum Beispiel --samy-bg, --samy-text, --samy-cta oder --samy-text-secondary. Damit passt deine Seite ohne eigene Farbwerte zur App.

Abstände

In Samy füllt deine Seite die ganze Fläche unter Samys schmaler Kopfzeile bis zum unteren Rand (Vollbild). Die Kopfzeile bleibt immer gleich und verdeckt nichts. Unten setzt Samy die CSS-Variable --samy-inset-bottom (Home-Leiste des iPhones) und gibt <html> diesen Abstand als Innenabstand; --samy-inset-top gibt es weiter, ist aber 0. Normalerweise musst du nichts tun.

  • Willst du die volle Höhe ohne Scrollen füllen, rechne die Abstände heraus: min-height: calc(100vh - var(--samy-inset-top) - var(--samy-inset-bottom)).
  • Willst du alles selbst regeln, schreib <html data-samy-insets="manual"> und nutz die Variablen an passender Stelle.

Speicher

samy.storage speichert kleine Texte auf dem Gerät, getrennt nach Konto und App. Teilen sich zwei Konten ein Handy, sieht keins die Einträge des anderen. Das unterscheidet ihn vom normalen localStorage der Seite. Bis zu 100 Einträge je App, je höchstens 16 KB. Für Objekte nimm JSON.stringify und JSON.parse. Meldet sich jemand ab, bleiben die Einträge auf dem Gerät. Löscht er das Konto, sind sie wertlos, weil niemand sonst sie lesen kann.

Session-Token

Ein JWT, das sagt, wer die Seite gerade in Samy öffnet.

AlgorithmusHS256
GeheimnisSHA-256-Hash deines Schlüssels als Hex-Text in Kleinbuchstaben. Dasselbe wie bei der Webhook-Signatur.
Gültigkeit60 Sekunden
FeldInhalt
isshttps://api.packlets.app
audID deines Packlets. Dieselbe wie packlet_id in jedem Webhook-Ereignis.
subID des Nutzers in Samy. Bleibt für denselben Nutzer immer gleich.
emailE-Mail-Adresse des Nutzers.
nameName des Nutzers, kann fehlen.
iat, expAusgestellt und gültig bis, als Unix-Zeit.
jtiEindeutige Kennung dieses Tokens.

Prüf auf deinem Server immer drei Dinge: die Signatur, exp und dass aud dein Packlet ist. Erzeugst du in der App einen neuen Schlüssel, sind alle Tokens sofort ungültig.

Token von Samy prüfen lassen

Ohne eigene JWT-Bibliothek fragt dein Server Samy. Dieser Aufruf braucht keinen Schlüssel.

HTTP
POST https://api.packlets.app/v1/session-token/verify
Content-Type: application/json
FeldTypPflichtBedeutung
tokenTextjaDas Token aus dem Header Authorization der Anfrage an deinen Server.

Antwort bei einem gültigen Token:

JSON
{
  "valid": true,
  "packlet_id": "9d2f7c10-3a5b-4c8e-9f21-6b7d8e9f0a12",
  "page_url": "https://deine-seite.example/app",
  "user": { "id": "4f1c…", "email": "[email protected]", "name": "Du" },
  "expires_at": "2026-11-10T08:16:00.000Z"
}

Sonst Status 401:

JSON
{ "valid": false, "error": "invalid_token", "message": "Das Token ist ungültig oder abgelaufen." }

Achtung: Vergleich packlet_id mit der ID deines Packlets. Sonst nimmt dein Server auch Tokens an, die für ein fremdes Packlet ausgestellt wurden. Bedient dein Server viele Packlets (zum Beispiel eine App für alle Nutzer), prüf stattdessen page_url: Das ist die Seite, die das Packlet gerade zeigt.

Für den Aufruf gilt das Rate-Limit, gezählt je Adresse deines Servers.

samy.css

html
<link rel="stylesheet" href="https://packlets.app/embed/samy.css">

Das Stylesheet bringt die Schrift Lato, die Farben der App und diese Bausteine:

KlasseWofür
samyRahmen der Seite: 16 Pixel Innenabstand, Bausteine untereinander mit 12 Pixel Abstand, mindestens so hoch wie der Bildschirm.
samy-title, samy-bigÜberschrift (20 px) und große Zahl (44 px).
samy-sub, samy-label, samy-noteGrauer Nebentext, Abschnitts-Label in Großbuchstaben, kleiner Hinweis.
samy-row, samy-spread, samy-grow, samy-pushNebeneinander; an die Ränder verteilen; Platz füllen; nach unten schieben.
samy-buttonKnopf wie in der App, 44 px hoch. Dazu is-secondary (hell) und is-small (32 px).
samy-tabsUmschalter zwischen Ansichten. Der aktive Tab trägt aria-selected="true".
samy-listListe mit feinen Trennlinien, Zeilen mindestens 44 px hoch.
samy-tableTabelle. Zahlenspalten mit is-num rechtsbündig.
samy-fieldEingabefeld.
samy-chip, samy-dotKleines Kennzeichen; Status-Punkt grün, mit is-warn rot.
samy-barsKleines Balkendiagramm aus <i style="height: 40%">. Der aktive Balken trägt is-active.

Farben und Maße als Variablen:

VariableBedeutung
--samy-bgHintergrund
--samy-text, --samy-text-2, --samy-text-3Haupttext, Nebentext, noch hellerer Text
--samy-iconSymbole
--samy-cta, --samy-on-ctaDunkelblau der Knöpfe und die Schrift darauf
--samy-fieldHellgrau für Felder und helle Knöpfe
--samy-selected, --samy-barAusgewählter Tab, Balken im Diagramm
--samy-dividerTrennlinien
--samy-ok, --samy-warnGrün und Rot für Status
--samy-radiusEckenradius

Text lässt sich auf der Seite nicht markieren (user-select: none), wie in einer App. Eine fertige Beispielseite liegt unter /embed/beispiel.html.