メインコンテンツまでスキップ

APIの例

このページを使用して、各拡張フローに適したリクエストフォームをコピーしてください。

  • iframe ブリッジの例では、session.getstate.getfiles.get などの postMessage アクションが使用されます。
  • 特権的な例では、Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEYx-chastify-main-token を使用して独自の拡張バックエンドを使用します。
  • 開発者APIキーをiframeやブラウザコードに送信しないでください。
備考

iframeブリッジは、UIのブートストラップやリスクの低いセッション操作に使用されます。コンテキストの読み取り、拡張機能が所有する状態の保存、ファイルURLの解決などに使用します。

注意

ブラウザのiframeコードはユーザーが制御します。iframeブリッジリクエストを使用して、時間の適用/削除、タスクの完了、ロック解除ブロッカーの解除、ランタイムファイルのアップロード/削除、通知の送信、ログの書き込み、またはデバイスのコマンドを実行しないでください。

リクエストとレスポンスのフォーマット

安全なiframeブリッジアクションは、このブリッジリクエストエンベロープ内に送信されます。

{
"type": "chastify:ext:req", // required
"v": 1, // protocol version
"id": "req-123", // your unique request id
"nonce": "from-iframe-hash",
"action": "session.get",
"payload": {}
}

そして、あなたは以下を受け取ります。

{
"type": "chastify:ext:resp",
"v": 1,
"id": "req-123", // same id you sent
"ok": true,
"data": {}
}

okfalseである場合、error.codeerror.messageを確認します。

バックエンドのみの例では、サーバーからの通常のHTTPSリクエストを使用します。これらはiframeブリッジを経由して送信されません。

セッションとコンテキスト

session.get

ほぼすべての拡張機能フローで、まずこれを使用してください。

その機能:

  • ブリッジ接続が正常に機能していることを確認します。
  • セッションコンテキスト(ロック状態、ロール、拡張機能の設定、機能)を返します。
  • device.command をバックエンドに呼び出す前に、UI が使用できるデバイス機能情報を返します。

用途:

  • UIをブートストラップする。
  • 役割と権限に基づいて機能を有効化/無効化する。
  • デバイスコントロールを表示する前に、サポートされているデバイスコマンドを確認します。

アクションペイロードの例:

{
"action": "session.get",
"payload": {}
}

session.get応答の抜粋例(ランタイムロックデータ):

{
"ok": true,
"data": {
"lockData": {
"frozen": false,
"unlockable": false,
"trusted": true,
"taskAssigned": true,
"timeLockedSeconds": 1420,
"timeRemainingSeconds": 27800,
"maxTimeRemainingSeconds": 86400,
"taskPoints": 12,
"taskPointsRequired": 20,
"lockTitle": "Weekend Challenge",
"wearerUsername": "alice",
"keyholderUsername": "kh_bob",
"wearerLastSeenTimestamp": 1739640505123,
"keyholderLastSeenTimestamp": null
}
}
}

プライバシーに関する注意事項:

  • wearerLastSeenTimestamp / keyholderLastSeenTimestamp は、そのユーザーが showOnlineStatus !== false を持っている場合にのみ返されます。
  • オンラインステータスの表示が無効になっている場合、これらのフィールドは null になります。

バックエンド拡張フロー

これらの例は、安全な生産パターンを示しています。

  1. iframeはハッシュペイロードからmainTokensessionIdを読み取ります。
  2. iframeはそれらをバックエンドに送信します。
  3. バックエンドは、ゲーム、タスク、またはビジネスの状態を検証します。
  4. バックエンドは、アプリスコープの開発者APIキーとx-chastify-main-tokenを引数としてChastifyを呼び出します。

認証にはsessionIdのみを使用しないでください。mainTokenはブラウザから見える起動コンテキストとして扱い、開発者APIキーはバックエンド専用の秘密情報として扱ってください。

警告

バックエンドは、Chastify を呼び出す前に、自身のゲーム/タスクの状態を検証する必要があります。iframe から mainTokensessionId を渡しても、開いている拡張セッションを識別するだけで、ユーザーがチャレンジを完了したことを証明するものではありません。

セッションコンテキストを安全に取得する

iframe は、UI のブートストラップに安全なブリッジ session.get を使用できます。バックエンドで特権アクションを適用する前に同じコンテキストが必要な場合は、両方の認証情報を使用してバックエンドからコンテキストを取得してください。

iframe:

const hash = JSON.parse(decodeURIComponent(window.location.hash.slice(1)));

await fetch("/api/my-extension/session-context", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
mainToken: hash.mainToken,
sessionId: hash.sessionId
})
});

バックエンド:

app.post("/api/my-extension/session-context", async (req, res) => {
const { mainToken, sessionId } = req.body;

const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}`,
{
headers: {
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
}
}
);

if (!response.ok) {
return res.status(response.status).json(await response.json());
}

const context = await response.json();

res.json({
role: context.role,
config: context.extensionConfig,
lockData: context.lockData
});
});

拡張機能のバックエンドから時間を適用する

ブラウザはバックエンドに対して報酬または罰則の適用を要求する場合がありますが、ブラウザはアクションの有効性を判断してはなりません。まずはサーバー側でアクションの有効性を検証してください。

app.post("/api/my-extension/apply-reward", async (req, res) => {
const { mainToken, sessionId, runId } = req.body;

const run = await db.gameRuns.findUnique({ where: { id: runId } });
if (!run || run.sessionId !== sessionId || !run.serverVerifiedWin) {
return res.status(403).json({ error: "game_not_verified" });
}

const response = await fetch(
`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/action`,
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
name: "remove_time",
params: 300
})
}
);

if (!response.ok) {
return res.status(response.status).json(await response.json());
}

res.json(await response.json());
});

罰則にはadd_timeを、報酬にはremove_timeを使用してください。

サーバー検証済みゲーム完了

Simon Saysのようなゲームでは、クライアントから報告された勝利を鵜呑みにしないでください。サーバー側で実行を作成し、期待されるシーケンスまたはそのハッシュを保存し、Chastifyを呼び出す前に送信された入力を検証してください。

ブラウザ上で表示される記憶ゲームでは、人間が正直にプレイしたことを証明することはできません。なぜなら、ブラウザはゲームを表示するためにシーケンスを受信する必要があるからです。サーバー検証は、偽造されたChastify変異を防ぎ、報酬や罰則を適用する前に、バックエンドで実行ID、有効期限、難易度、頻度、スコアリング、リプレイ保護を強制できるようにします。

app.post("/api/simon/runs", async (req, res) => {
const { mainToken, sessionId } = req.body;
await verifySessionLaunch({ mainToken, sessionId });

const sequence = createSimonSequence();

const run = await db.simonRuns.create({
data: {
sessionId,
sequenceHash: hashSequence(sequence),
expiresAt: new Date(Date.now() + 5 * 60_000)
}
});

res.json({
runId: run.id,
sequence
});
});

app.post("/api/simon/runs/:runId/complete", async (req, res) => {
const { mainToken, sessionId, input } = req.body;
await verifySessionLaunch({ mainToken, sessionId });

const run = await db.simonRuns.findUnique({ where: { id: req.params.runId } });

if (!run || run.sessionId !== sessionId || run.expiresAt < new Date()) {
return res.status(403).json({ error: "run_invalid" });
}

const won = hashSequence(input) === run.sequenceHash;
await db.simonRuns.update({
where: { id: run.id },
data: { completedAt: new Date(), serverVerifiedWin: won }
});

if (won) {
await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "simon_says_wins",
amount: 1
})
});
}

res.json({ won });
});

実行時のストレージスキーマは、お客様独自のものとなります。verifySessionLaunch は、sessionId を信頼する前に、アプリスコープの開発者 API キーと x-chastify-main-token を使用して Chastify を呼び出す必要があります。重要なルールは、Chastify の変更は、バックエンドが起動と結果を検証した後にのみ発生するということです。

予定されている要件

スケジュール、実行頻度チェック、および証明検証は、バックエンドが管理します。Chastify は、バックエンドが要件が満たされたと判断した後、信頼できる進捗状況を記録したり、ロック解除ブロッカーを更新したりする場合にのみ使用してください。

async function recordDailyRequirementProgress({ sessionId, mainToken, userId }) {
const completed = await db.dailyCheckins.exists({
where: {
userId,
day: new Date().toISOString().slice(0, 10),
verified: true
}
});

if (!completed) return;

await fetch(`https://chastify.net/api/extensions/sessions/${encodeURIComponent(sessionId)}/requirements/progress`, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.CHASTIFY_APP_DEVELOPER_KEY}`,
"x-chastify-main-token": mainToken
},
body: JSON.stringify({
key: "daily_checkin",
amount: 1
})
});
}

現在インストールされている拡張機能セッションAPIでは、x-chastify-main-tokenの有効なiframe起動トークンが必要です。起動トークンは現在10時間後に期限切れとなります。トークンの有効期限が切れた後に実行されるスケジュール済みジョブについては、保留中の証明を保存し、次の有効な拡張機能起動時に進捗状況を送信するか、無人バックグラウンド処理用に設計されたファーストパーティ/組み込みのサーバーフローを使用してください。

注意

スケジュールされたジョブが自社サーバー上で実行されるからといって、信頼できるジョブとして扱わないでください。ジョブは、要件の進捗状況を記録する前に、サーバー側での検証、実行頻度チェック、リプレイ攻撃対策、および有効なChastify認証パスが必要です。

通知

notifications.custom

拡張機能のバックエンドからこれを使用して、装着者、キーホルダー、またはその両方にカスタム拡張機能通知を送信します。

終点:

POST /api/extensions/sessions/:sessionId/notifications/custom
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

本文の例:

{
"title": "Extension Reminder",
"message": "Your next challenge is ready.",
"showPageOverlay": false,
"target": "both"
}

注記:

  • showPageOverlay はデフォルトで false になります。
  • target はデフォルトで wearer になります。
  • APIは通知タイプextension_app_messageを作成します。

拡張州

拡張機能の状態とは、現在のロックセッションに関して、拡張機能が所有するJSONデータのことです。 状態の書き込みはバックエンドのみで可能であり、アプリスコープの開発者APIキーとセッションmainTokenが必要です。iframeはstate.getを使用して状態を読み取ることができますが、状態を直接書き込むことはできません。

state.put

その機能:

  • 状態オブジェクト全体を新しいdataオブジェクトに置き換えます。
  • バックエンドの認証情報が必要です。

使用時期:

  • 初回セーブ。
  • 既に完全な新しい状態が取得できている場合は、完全に上書きしてください。

例:

curl -X PUT "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 1,
"notes": "first test"
}
}'

フィールドノート:

  • payload.data: 拡張機能が必要とする有効なJSON値/オブジェクト。
  • Avoid Mongo-unsafe key names ($ prefix or keys containing .).
  • 状態はセッションスコープであり、サイズは拡張機能アプリの stateMaxBytes によって制限され、デフォルトは 64 KiB です。
  • ファイルIDはバイナリファイルや署名付きURLではなく、ステートに保存してください。メディアをレンダリングする前に、files.getを使用して署名付きURLを更新してください。

state.patch

その機能:

  • 既存の状態にJSONマージパッチを適用します。
  • 変更された鍵のみを送信してください。
  • バックエンドの認証情報が必要です。

使用時期:

  • ユーザー操作に基づく増分更新。
  • 全てを再送信せずに、特定のフィールドのみを更新する。

例:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"counter": 2
}
}'

state.get

その機能:

  • 現在の拡張機能の状態を読み取ります。
  • iframeブリッジ経由で利用可能です。

使用時期:

  • iframeの読み込み時。
  • 書き込み後、ローカルUIを再同期したい場合。

例:

{
"action": "state.get",
"payload": {}
}

ファイルストレージ

パズル画像、生成されたプレビュー、チャレンジ写真などのバイナリメディアには、files.* を使用してください。返された file.id は、バックエンドで書き込まれた状態または独自のデータベースに保存してください。file.signedUrl を使用してレンダリングし、後で iframe が読み込まれたときに、署名付き URL を files.get で更新してください。

ロック/セッションが存在する前に実行されるセットアップ画面では、files.upload の代わりにステージングされたアップロードが使用されます。ステージングされたファイルは、拡張機能の設定が provider: "chastify_storage" および fileId 参照とともに保存されるまで一時的なものです。Chastify は、ロック/テンプレートの保存時にこれらのファイルを自動的に取得します。ブラウザ側で別途取得呼び出しを行う必要はありません。

バックエンドからの状態/ファイル分割の例:

curl -X PATCH "https://chastify.net/api/extensions/sessions/$SESSION_ID/state" \
-H "Authorization: Bearer $DEVELOPER_KEY" \
-H "x-chastify-main-token: $MAIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"puzzleImageFileId": "file_record_id"
}
}'

後で、表示前に画像を解決します。

const state = await bridge.request("state.get", {});
const file = await bridge.request("files.get", {
fileId: state.data.puzzleImageFileId
});
image.src = file.file.signedUrl;

files.capabilities

アップロードコントロールを表示する前に、こちらを確認してください。

{
"action": "files.capabilities",
"payload": {}
}

ランタイム files.upload

ランタイムファイルのアップロードは拡張機能のセッションデータを変更するため、iframe ブリッジコマンドではありません。アプリスコープの開発者 API キーと x-chastify-main-token を使用して、バックエンドからランタイムファイルをアップロードしてください。

files.get

安定したファイルIDから、署名済みのR2 URLを1つ更新します。

{
"action": "files.get",
"payload": {
"fileId": "file_record_id"
}
}

files.list

{
"action": "files.list",
"payload": {}
}

ランタイムfiles.deleteも、アップロードと同じ理由でバックエンド専用です。

ロックアクション

時間を追加する

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • deltaSecondsに応じて、ロックカウントダウンに時間を追加または削除します。

使用時期:

  • 報酬/罰則ボタン。
  • ゲームの結果(勝利すると時間が加算され、敗北すると時間が減算される)。

例:

{
"name": "add_time",
"params": 300 // +300 sec = +5 minutes
}

フィールドノート:

  • プラスの価値は時間を生み出す。
  • 負の値は時間を削除します(サーバーのルールで許可されている場合)。

凍結

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • ロックの進行を一定時間停止します。

使用時期:

  • クールダウンの仕組み。
  • 報酬チェックポイント。

例:

{
"name": "freeze",
"params": { "durationSeconds": 120 }
}

durationSeconds なしで呼び出すこともできます。

{
"name": "freeze",
"params": {}
}

フィールドノート:

  • durationSecondsは省略可能です。
  • 省略した場合、現在のデフォルト値は3600秒(1時間)です。
  • 許容範囲は60秒から86400秒です。

凍結解除

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • アクティブなフリーズを終了し、タイマーの通常の動作を再開します。

使用時期:

  • 拡張機能ワークフローにおける手動オーバーライド。
  • 「フリーズ解除」コントロール。

例:

{
"name": "unfreeze",
"params": {}
}

さらし台

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • アクティブなロックセッションに対して、拘束期間を開始します。

使用時期:

  • タスク/チャレンジに失敗した際のペナルティの仕組み。
  • ロック操作を一時的に制限するエスカレーションフロー。

例:

{
"name": "pillory",
"params": {
"durationSeconds": 600,
"reason": "Missed scheduled check-in"
}
}

フィールドノート:

  • namepilloryでなければなりません。
  • params.durationSecondsは必須です。
  • params.reasonは省略可能です。
  • セッション設定でさらし台を有効にする必要があります。

さらし台を終わらせる

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • 現在実施中のさらし台セッションを直ちに終了する。

例:

{
"name": "pillory.end",
"params": {}
}

フィールドノート:

  • namepillory.endでなければなりません。
  • 錠が現在さらし台にかけられていない場合、pillory_not_active のエラーで失敗します。

タスクを割り当てる

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • 拡張機能ロジックから、装着者向けのアクティブなタスク実行を作成します。
  • 既に実行中のタスクを上書きできます。

例:

{
"name": "task.assign",
"params": {
"taskText": "Clean your room",
"points": 10,
"verificationRequired": true,
"durationSeconds": 1800
}
}

フィールドノート:

  • taskTextは必須です。
  • pointsはオプションであり、サーバー側で制限されます。
  • verificationRequired はデフォルトで false になります。
  • durationSecondsはオプションです(0はタイマー不要を意味します)。
  • ロックでタスクモジュールが有効になっている必要があります。

タスクタイマーを開始します

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • 現在アクティブなタイマー付きタスクのカウントダウンウィンドウを開始または再開します。

例:

{
"name": "task.start_timer",
"params": {}
}

フィールドノート:

  • アクティブなタスクの実行が必要です。
  • 現在のタスクに実行期間が設定されていない場合、失敗します。

タスクを完了する

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • 実行中のタスクが完了または失敗としてマークされます。

例(成功例):

{
"name": "task.complete",
"params": {
"successful": true
}
}

例(失敗):

{
"name": "task.complete",
"params": {
"successful": false,
"reason": "Did not finish in time"
}
}

トリガーの一時的な開放

エンドポイント: POST /api/extensions/sessions/:sessionId/action

その機能:

  • 拡張機能のロジックから一時的な衛生管理のためのウィンドウを開始します。

例:

{
"name": "hygienic_unlock.start",
"params": {
"durationSeconds": 900
}
}

フィールドノート:

  • ロックに衛生的な開錠機能が有効になっている必要があります。
  • 衛生的な開口部が既に開いている場合は失敗します。
  • durationSecondsはオプションです。省略した場合は、ロックのデフォルト値が使用されます。

メタデータとホームアクション

metadata.patch

その機能:

  • ロックページUIで使用される拡張機能メタデータを保存します。
  • unlockBlockersおよびhomeActionsをサポートします。
  • 拡張機能を開いた際のディープリンク動作のために、homeActions[].intentをサポートします。

使用時期:

  • 拡張機能が所有するロック/セッション解除条件を適用します。
  • ロックページに、意図を持って拡張機能を開くクイックアクションを追加します。
  • ユーザーがホームアクションをクリックした際に、特定の画面/ワークフローに直接誘導する。

終点:

PATCH /api/extensions/sessions/:sessionId/metadata
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

本文の例:

{
"unlockBlockers": ["Finish extension task"],
"homeActions": [
{
"slug": "tasks",
"title": "Tasks",
"description": "Open tasks panel",
"intent": {
"type": "open_panel",
"title": "Tasks",
"message": "Open tasks panel",
"payload": {
"panel": "regular-actions"
}
}
}
]
}

フィールドノート:

  • unlockBlockers: 拡張機能からのアクティブなロック/セッション解除ブロッカーのリスト。
  • 複数のブロッカーを一度に含めることができます(最大25個)。満たされていない条件ごとに1つずつ含めてください。
  • 有効な拡張機能に何らかのブロック要因が存在する限り、ロック解除はブロックされたままになります。
  • Chastifyは、ロックセッションのためにすべての内線からのブロッカーを集約します。
  • 拡張機能は、自身のメタデータ内でのみ、自身のブロッカーを追加/削除するようにしてください。
  • 他の拡張機能のブロッカーを削除しないでください。独自の条件が満たされた場合にのみ、配列をクリアしてください。
  • homeActions: ロック画面にクイックアクションボタンが表示されます。
  • homeActions[].slug: あなたのアクションの安定したID。
  • homeActions[].title: ユーザー向けラベル。
  • homeActions[].description: オプションのヘルパーテキスト。
  • homeActions[].intent: 拡張機能を開いたときに渡されるオプションのディープリンク命令。
  • 拡張機能カードのユーザーインターフェースでは、これらのアクションはアクションタイトル(内部的にはスラッグによってキー付けされている)ごとにメニュー/リストとして表示されます。
  • ユーザーがいずれかをクリックすると、Chastify が拡張機能を開き、次の情報を渡します。
    • homeActionSlug
    • homeAction(選択されたアクションオブジェクト)
    • intent(正規化されたインテントオブジェクト) これにより、拡張機能は読み込み時に適切なビュー/アクションに即座にルーティングできます。

インテント:開発者アプリの例

拡張機能アプリでこのパターンを使用すると、読み込み時にメニュークリックの意図に反応できます。

import { useEffect, useRef } from "react";
import { parseHashPayload, type IframeHashPayload } from "../lib/ChastifyBridge";

export function useHomeActionIntent(
payload: IframeHashPayload,
routeToPanel: (panel: string) => void,
showToast: (message: string) => void,
) {
const handledRef = useRef<string>("");

useEffect(() => {
const homeActionSlug = payload?.homeActionSlug ?? null;
if (!homeActionSlug) return;

// Prevent duplicate handling if component re-renders.
const key = `${payload.lockId || "lock"}:${homeActionSlug}`;
if (handledRef.current === key) return;
handledRef.current = key;

const intent = payload?.intent ?? payload?.homeAction?.intent ?? null;
if (!intent) return;

if (intent.type === "open_panel") {
const panel = String(intent.payload?.panel || "");
if (panel) routeToPanel(panel);
return;
}

if (intent.message) {
showToast(String(intent.message));
}
}, [payload, routeToPanel, showToast]);
}

// Example app bootstrap
const payload = parseHashPayload();
if (!payload) throw new Error("Missing iframe hash payload");

この例が行うこと:

  • iframeハッシュペイロードからhomeActionSlug + intentを読み取ります。
  • 各クリックは、ロック/アクションスラグごとに一度だけ処理されます。
  • インテントがopen_panelの場合、パネルにルーティングします。
  • カスタムインテントタイプの場合、インテントメッセージを表示するようにフォールバックします。

デバイスコマンド

device.command

その機能:

  • デバイス制御コマンドを送信します(そのセッション/デバイスでサポートされている場合)。
  • 拡張機能がChastifyを介して標準化された衝撃/振動動作をトリガーできるようにします。

使用時期:

  • 拡張機能ロジックから、サポートされている衝撃/振動コマンドをトリガーします。
  • インタラクティブな拡張機能(ゲーム、罰則、報酬、ルーティンなど)を構築する。

終点:

POST /api/extensions/sessions/:sessionId/device-command
Authorization: Bearer YOUR_APP_SCOPED_DEVELOPER_KEY
x-chastify-main-token: MAIN_TOKEN_FROM_IFRAME_HASH

本文の例:

{
"command": "shock.start",
"params": {
"durationSeconds": 30,
"intensityPct": 50
}
}

よく使うコマンド:

  • パラメータ付きのshock.start: { durationSeconds, intensityPct, message? }
  • shock.stop
  • パラメータ付きのvibration.start: { durationSeconds, intensityPct, frequencyPct?, message? }
  • vibration.stop
  • all.stop
  • shock.random.set(パラメータ:{ enabled, minIntensityPct?, maxIntensityPct?, message? }(LockinkはAA-A1012のみ))
  • shock.berserk.set(パラメータ:{ enabled, message? }(LockinkはAA-A1012のみ))

推奨フロー:

  1. ロード時にsession.getを呼び出します。
  2. deviceControl.supportedCommandsからデバイスの機能を読み取ります。
  3. サポートされているコマンドのコントロールのみを表示します。
  4. 検証済みの値とともにdevice.commandを送信してください。
  5. ライブUI状態については、応答activeフラグを更新または信頼してください。

パラメータガイダンス:

  • durationSeconds: サーバーは安全な制限に制限します (現在のポリシーの最大値は 300 秒です)。
  • intensityPct: 期待されるパーセンテージ値 (1-100 形式の入力、サーバー側で制限)。
  • frequencyPct: 振動フローの期待パーセンテージ値 (1-100) (サーバー側でクランプ)。
  • ランダムモードの場合は、minIntensityPct <= maxIntensityPctであることを確認してください。

より安全なUIパターンの例:

// 1) Get capabilities first
const session = await bridge.request("session.get", {});
const supported = new Set(session?.deviceControl?.supportedCommands ?? []);

// 2) Only call command if supported
if (supported.has("shock.start")) {
await bridge.request("device.command", {
command: "shock.start",
params: { durationSeconds: 30, intensityPct: 50 }
});
}

重要:

  • まずsession.getを呼び出し、サポートされているコマンドを読み取ってください。
  • 現在のセッションでサポートされているコマンドのコントロールのみを表示します。
  • コマンドパラメータを送信する前に、ユーザー入力を検証してください。
  • device.command は、拡張セッションへの書き込み権限 (locks:write) を必要とします。
  • ブリッジ/サーバーのエラー(insufficient_scope、サポートされていないコマンド、検証エラーなど)を適切に処理します。

延長要件

拡張機能の要件により、拡張機能は、ロックの「今日の進捗状況」UIに表示される定期的な完了ルールを定義し、装着者が期限を逃した場合にペナルティを適用できます。

サーバーが信頼する拡張機能のアクティビティには、これを使用します。例:

  • 1日に1つのパズルを完成させましょう。
  • 2日ごとに3回のチェックインを完了してください。
  • 週に5つの拡張活動を完了してください。

構成形状

要件は、拡張セッション設定のextensionRequirementsに格納されています。

{
"extensionRequirements": {
"enabled": true,
"metric": "completion",
"requiredCount": 1,
"cadence": {
"every": 1,
"unit": "day"
},
"punishment": {
"type": "add_time",
"seconds": 900,
"reason": "Missed extension requirement"
}
}
}

分野:

  • enabled: 定期的な要件のオン/オフを切り替えます。
  • metric: 安定カウンタ名。completionwinverificationなどのシンプルな名前を使用してください。
  • requiredCount: ウィンドウ内で発生する必要のある信頼できるイベントの数。
  • cadence.every: インターバルサイズ。
  • cadence.unit: day または week
  • ウィンドウのタイムゾーンは、ウェアラーの設定済みタイムゾーンである User.timezone から Chastify によって解決されます。拡張機能の設定でタイムゾーンをハードコーディングしないでください。
  • punishment.type: noneadd_timefreeze、またはpillory
  • punishment.seconds: add_timefreeze、またはpilloryに対する罰則の期間。
  • punishment.reason: オプションの監査/デバッグ理由。

進捗モデル

要件の進捗状況は、iframe のローカル状態ではなく、信頼できるサーバー側の状態です。

要件を完了としてマークするために、state.patch / state.put を使用しないでください。これらの操作はバックエンド専用の一般的な状態書き込みであり、要件の進捗状況を記録する API ではありません。要件の進捗状況は、サーバーがカウントすべきイベントを検証した後にのみ記録する必要があります。

例えば:

  • パズル拡張機能は、サーバーが署名済みのパズル実行結果と完了状態を検証した後にのみ、進行状況を記録するべきである。
  • 検証拡張機能は、サーバーが提出された証明を受け入れた後にのみ、進捗状況を記録するべきである。
  • ゲーム拡張機能は、バックエンドがゲームの結果または信頼できる完了イベントを検証した後にのみ、進行状況を記録するべきである。

実行時動作

設定時:

  • ロックダッシュボードには、今日の進捗状況における要件が表示されます。
  • Chastifyは、拡張セッションごと、およびケイデンスウィンドウごとに進捗状況を追跡します。
  • スケジュールされた要件ジョブは、完了したウィンドウを評価し、未完了のウィンドウごとに設定されたペナルティを一度適用します。
  • 罰則はウィンドウごとに冪等性を持つため、再試行しても重複した罰則は加算されません。
  1. 拡張機能の設定/構成UIでextensionRequirementsを設定してください。
  2. 実行時起動時に、session.get を呼び出してアクティブな設定を読み取ります。
  3. UI上で拡張機能のアクティビティを完了してください。
  4. その拡張機能に対応する信頼できるバックエンドルートに完了情報を送信する。
  5. バックエンドでイベントを検証し、要件の進捗状況を記録します。
  6. 完了後、ローカルUIをsession.get / state.getで更新してください。

重要:

  • state.*は拡張機能が所有するストレージとしてのみ扱ってください。進捗状況、試行回数、報酬、罰則については、専用の信頼できるAPIを使用してください。
  • 要件に関して、クライアント側だけの完了フラグを信用してはいけません。
  • metric という名前は安定させてください。メトリックを変更すると、別のバケットにカウントされるようになります。
  • Chastifyは、ケイデンスウィンドウに装着者の設定済みタイムゾーンを使用します。ユーザーにタイムゾーンが設定されていない場合、サーバーはUTCにフォールバックします。
  • 監査ログには、罰則の範囲を限定し、説明可能な形で記載するようにしてください。
  1. 起動時にsession.getを呼び出す。
  2. state.getで状態を読み取ります。
  3. 必要に応じて、バックエンドからPUT/PATCH /stateを使用して状態書き込みを実行してください。
  4. ロック/デバイス操作は、UI上でサポートされ、かつ表示されている場合にのみ実行します。
  5. 要件に基づくアクティビティについては、完了を信頼できるバックエンドルートに報告してください。
  6. 重要な書き込みや操作の後には、ローカルビューを更新してください。