首页龙虾技能列表 › Web Service Onboarding — Web 服务入职

Web Service Onboarding — Web 服务入职

v1.0.2

Web 服务入职工具。

0· 70·1 当前·1 累计
by @nissan (Nissan Dookeran)·MIT-0
下载技能包
License
MIT-0
最后更新
2026/3/28
安全扫描
VirusTotal
Pending
查看报告
OpenClaw
可疑
medium confidence
The skill's purpose (automated signups and secure storage) is plausible, but its instructions handle sensitive secrets (email creds, WebAuthn exports, 1Password storage) while failing to declare required binaries/credentials and instructing potentially unsafe file I/O, so several mismatches deserve attention before installing.
评估建议
This skill automates account creation and handles very sensitive secrets (email inbox access, passkeys, API keys, and 1Password storage). Before installing or invoking it, ask the publisher to: 1) list required binaries and exact environment variables (Playwright/Chrome, Node, 1Password CLI/OP session env, Proton Bridge/IMAP creds); 2) explain how 1Password authentication is performed (which env var or interactive flow) and confirm it will not leak tokens; 3) avoid writing raw credentials to wor...
详细分析 ▾
用途与能力
The skill's claimed purpose—automating signups, email verification, passkey handling, API key generation, and storing secrets in 1Password—matches the content of SKILL.md. However, the skill does not declare any required binaries, environment variables, or credentials (e.g., Playwright/Chromium, 1Password CLI/session token, Proton Bridge or IMAP credentials), even though the instructions clearly require them. That omission is an inconsistency: a legitimate onboarding skill should list the external tools and secrets it needs.
指令范围
The SKILL.md instructs the agent to perform sensitive operations: create browser contexts, add a Playwright CDP virtual authenticator, export WebAuthn credentials to a local file (/tmp/webauthn-creds.json), fetch verification links via an IMAP bridge on localhost, and save API keys into 1Password. These operations access, create, and persist secrets. The instructions do not explain how to authenticate to 1Password or how to protect exported passkey files (which are written to /tmp, often world-readable). The skill also assumes access to a Proton Bridge or local IMAP proxy at 127.0.0.1:1143 without documenting credentials or setup. This scope creep and lack of safe-handling guidance is concerning.
安装机制
There is no install spec (instruction-only), so nothing will be written automatically to disk by an installer — this lowers supply-chain risk. However, the runtime instructions depend on external tooling (browser automation like Playwright/CDP, Node runtime, 1Password CLI, Proton Bridge) and will write temporary files. The absence of an install spec means the skill expects those tools to already exist; the missing dependency declarations are a transparency gap.
凭证需求
No environment variables or primary credential are declared, yet the workflow requires secrets and auth to external/local services: IMAP/email credentials or Proton Bridge config, 1Password session tokens or CLI auth, and possibly cloud provider credentials for some services. The skill also writes exported passkeys and presumably API keys to disk before moving them to 1Password. Requesting unspecified credentials or accessing local services without declaring them is disproportionate and raises risk of misconfiguration or accidental secret exposure.
持久化与权限
The skill is not marked always:true and is user-invocable, so it does not have elevated forced persistence. Autonomous invocation is allowed (platform default) but that alone is not flagged. The SKILL.md does instruct writing persistent artifacts (files under /tmp, 1Password entries) but it does not attempt to modify other skills or global agent config.
安全有层次,运行前请审查代码。

License

MIT-0

可自由使用、修改和再分发,无需署名。

运行时依赖

无特殊依赖

版本

latestv1.0.22026/3/28

Security: added security_notes to clarify legitimate usage of network/credential/encoding patterns. Prevents false-positive scanner flags.

● Pending

安装命令 点击复制

官方npx clawhub@latest install web-service-onboarding
镜像加速npx clawhub@latest install web-service-onboarding --registry https://cn.clawhub-mirror.com

技能文档

Core Pattern

Send signup email → Verify email → Complete registration → 
Generate API keys → Store securely in 1Password → Wire .env

Do this in a single unbroken browser session. Never close the browser between steps.


Critical Rules (Learned Hard Way — Turnkey, 2026-03-25)

1. One 会话, 否 gaps

  • Complete signup 和 API 键 creation 和 secret storage 在...中 相同 browser 会话
  • 关闭 browser 仅 之后 credentials saved 到 1Password
  • 如果 browser closes 之前 credentials extracted, 您've lost access — passkey/会话 gone

2. Email alias trap

  • Proton Mail (和 many providers) treat 用户+alias@domain.com 作为 相同 用户
  • 如果 服务 已经 有 账户 对于 用户@domain.com, alias 将 路由 到 existing 账户
  • Always check whether 服务 resolves aliases 之前 使用 them 对于 fresh 账户
  • 使用 completely 不同 email (不同 domain, 不同 provider) 对于 truly separate 账户

3. WebAuthn virtual authenticator ephemeral

  • Playwright's WebAuthn.addVirtualAuthenticator creates 在...中-memory credential store
  • passkey registers 仅 有效 对于 browser process
  • 如果 您 关闭 browser 和 reopen , credential gone forever
  • 仅 way 到 reuse 到 导出 credential 之前 closing, 然后 re-导入 在...上 下一个 run
  • 导出 immediately 之后 registration:
  const creds = await cdp.send('WebAuthn.getCredentials', { authenticatorId });
  fs.writeFileSync('/tmp/webauthn-creds.json', JSON.stringify(creds));
  
  • Re-导入 在...上 下一个 会话:
  const saved = JSON.parse(fs.readFileSync('/tmp/webauthn-creds.json'));
  for (const cred of saved.credentials) {
    await cdp.send('WebAuthn.addCredential', { authenticatorId, credential: cred });
  }
  

4. Email verification 链接-based, 不 always OTP

  • Don't assume OTP 输入框 fields — check actual email body 第一个
  • Turnkey, Vercel, Railway, Render 所有 发送 magic links 不 codes
  • 解析 email body 带有 quoted-printable decoding 之前 extracting URLs
  • Watch 对于 soft line breaks (=\n) 在...中 QP-encoded emails

5. 会话 cookies tied 到 authenticator

  • 如果 您 complete signup 在...中 context 和 try 到 使用 会话 在...中 context B, won't work
  • Cookies + passkey credential 必须 stay 在...中 相同 browser context

6. Internal APIs 不 公开 APIs

  • app.服务.com/internal/api/ endpoints require 会话 cookies
  • api.服务.com/公开/v1/ endpoints require API 键 stamping
  • 您 可以't call 公开 API endpoints 到 bootstrap 如果 您 有 否 API 键 尚未
  • 仅 internal API (Cookie-auth) accessible 从 authenticated browser 会话

Workflow

Phase : 发送 signup email (clean context — 否 cookies)

const ctxClean = await browser.newContext({ storageState: undefined });
const page = await ctxClean.newPage();
await page.goto('https://service.com/signup');
// fill email, click continue
// close ctxClean immediately after submitting
await ctxClean.close();

为什么 separate? Prevents existing 会话 cookies 从 hijacking signup flow.

Phase B: Complete signup + 保存 API keys (相同 context throughout)

const ctx = await browser.newContext({ storageState: undefined });
const page = await ctx.newPage();
const cdp = await ctx.newCDPSession(page);

// Set up virtual authenticator BEFORE navigating await cdp.send('WebAuthn.enable', { enableUI: false }); const { authenticatorId } = await cdp.send('WebAuthn.addVirtualAuthenticator', { options: { protocol: 'ctap2', transport: 'internal', hasResidentKey: true, hasUserVerification: true, isUserVerified: true, automaticPresenceSimulation: true, } });

// Navigate to verify link await page.goto(verifyUrl);

// Complete signup steps...

// IMMEDIATELY export credential after passkey registration const creds = await cdp.send('WebAuthn.getCredentials', { authenticatorId }); fs.writeFileSync('/tmp/webauthn-creds.json', JSON.stringify(creds)); console.log('Credentials backed up:', creds.credentials?.length);

// Continue to API key + wallet creation IN SAME SESSION // ...save API keys...

// Close browser ONLY after saving everything to 1Password


Email Fetching 通过 Proton Bridge IMAP

function fetchLatestTurnkeyLink(host='127.0.0.1', port=1143, user, pass) {
  return new Promise((resolve) => {
    const socket = net.connect(port, host);
    let buf='', tls2=null, step=0, body=[], inBody=false;
    const t = setTimeout(() => { try{(tls2||socket).destroy()}catch(e){}; resolve(null); }, 22000);
    function send(cmd) { (tls2||socket).write(cmd+'\r\n'); }
    function onData(data) {
      buf += data.toString();
      const lines = buf.split('\r\n'); buf = lines.pop();
      for (const l of lines) {
        if (inBody) body.push(l);
        if (step===0 && l.includes('OK')) { step=1; send('a1 STARTTLS'); }
        else if (step===1 && l.includes('a1 OK')) { tls2=tls.connect({socket,rejectUnauthorized:false}); tls2.on('data',onData); step=2; send(a2 LOGIN "${user}" "${pass}"); }
        else if (step===2 && l.includes('a2 OK')) { step=3; send('a3 SELECT INBOX'); }
        else if (step===3 && l.includes('a3 OK')) { step=4; send('a4 SEARCH ALL'); }
        else if (step===4 && l.startsWith(' SEARCH')) {
          const nums = l.replace(' SEARCH','').trim().split(' ').filter(Boolean);
          step=5; inBody=true; send(a5 FETCH ${nums[nums.length-1]} (BODY[TEXT]));
        }
        else if (step===5 && l.includes('a5 OK')) {
          clearTimeout(t); (tls2||socket).end();
          // Decode quoted-printable
          const decoded = body.join('\n')
            .replace(/=\r?\n/g, '')
            .replace(/=([0-9A-Fa-f]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
          // Extract redirect URLs
          const urls = [...decoded.matchAll(/https:\/\/service\.com\/redirect\?token=[^\s"<>)]+/g)].map(m=>m[0]);
          resolve(urls[0] || null);
        }
      }
    }
    socket.on('data', onData);
    socket.on('error', () => { clearTimeout(t); resolve(null); });
  });
}

键: Pattern-match redirect URL 到 服务's domain, 不 generic URL.


Proton Mail Setup

  • IMAP host: 127.0.0.1, port: 1143, STARTTLS
  • Credentials 在...中 1Password: op://OpenClaw/Proton Bridge - Monk Fenix/...
  • Bridge 必须 running: ps aux | grep -i bridge

输入框/表单 Filling — 使用 Native 值 Setter

Standard element.fill() sometimes fails on React inputs. Use this:

await page.evaluate((value) => {
  const input = document.querySelector('input');
  Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')
    .set.call(input, value);
  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));
}, value);

按钮 Clicking — Scroll 进入 视图 第一个

Buttons outside viewport fail with element is outside of the viewport. Always scroll:

await page.evaluate((text) => {
  const btn = [...document.querySelectorAll('button')]
    .find(b => b.textContent?.toLowerCase().includes(text) && !b.disabled);
  if (btn) { btn.scrollIntoView(); btn.click(); }
}, buttonText);

之后 Successful Authentication — 保存 API keys

For services with internal browser APIs:

// Call authenticated internal API from page context
const data = await page.evaluate(async () => {
  const r = await fetch('/internal/api/v1/whoami');
  return r.json();
});
// data.organizationId, data.userId, etc.

Creating API Keys / Resources 通过 Internal API

Once authenticated (cookie present), call internal endpoints from the page context:

const result = await page.evaluate(async ({ orgId, publicKey }) => {
  const r = await fetch('/tkhq/api/v1/activities', {  // adjust per service
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'ACTIVITY_TYPE_CREATE_API_KEYS',
      organizationId: orgId,
      parameters: { apiKeys: [{ apiKeyName: 'my-key', publicKey, curveType: 'API_KEY_CURVE_P256' }] }
    })
  });
  return { status: r.status, body: await r.text() };
}, { orgId, publicKey });

Important: Internal endpoints vary per 服务. 之前 creating resources, capture network traffic 到 learn real endpoint:

page.on('request', req => {
  if (req.method() !== 'GET' && req.url().includes(serviceDomain))
    console.log(req.method(), req.url());
});

Saving 到 1Password

op item create \
  --vault OpenClaw \
  --title "Turnkey API Credentials — Reddi Agent Protocol" \
  --category "API Credential" \
  "org_id[text]=$ORG_ID" \
  "user_id[text]=$USER_ID" \
  "api_public_key[text]=$API_PUB" \
  "api_private_key[password]=$API_PRIV" \
  "wallet_id[text]=$WALLET_ID" \
  "wallet_address[text]=$WALLET_ADDR"

Signup Registry (MANDATORY)

Before starting any signup, add an entry to the Notion signup registry:

  • DB: 322eb552-581a-81dc-adbc-fabb7af1d311
  • Fields: 服务 name, email used, 日期, purpose
  • non-negotiable per POLICIES.md

服务-Specific Notes

Turnkey (app.turnkey.com)

  • Email 验证: 链接-based (magic 链接, 不 OTP)
  • Auth: WebAuthn passkey (virtual authenticator works)
  • Post-signup API: /tkhq/api/v1/activities (internal, Cookie-auth)
  • 公开 API: api.turnkey.com/公开/v1/ requires X-Stamp (signed 请求)
  • Email aliases (+tag) 地图 到 相同 Turnkey 账户 — 使用 不同 provider 对于 separate orgs
  • Org created 在...中 one run: b7378687-cf82-45ab-a46c-7dda9239001d (Reddi Agent Protocol)

Generic patterns

  • Vercel: email OTP 或 GitHub OAuth
  • Railway: GitHub OAuth (否 email signup)
  • Supabase: email + 密码, 然后 API 键 在...中 dashboard
  • Fly.io: email + credit card, CLI bootstrap preferred

Pre-flight Checklist

Before starting any signup:

  • [ ] Added 到 Notion signup registry
  • [ ] Confirmed email 可用 (不 已经 used 对于 服务)
  • [ ] Email aliases: 做 服务 collapse them? (test 第一个)
  • [ ] IMAP readable 对于 email provider 正在 used
  • [ ] 1Password vault accessible
  • [ ] Proton Bridge running (如果 使用 Proton)
  • [ ] Sufficient budget 对于 paid tier (如果 applicable) — ask Nissan 第一个
数据来源:ClawHub ↗ · 中文优化:龙虾技能库
OpenClaw 技能定制 / 插件定制 / 私有工作流定制

免费技能或插件可能存在安全风险,如需更匹配、更安全的方案,建议联系付费定制

了解定制服务