[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M792SWFH9R3XRW6VFD8F7GVM":3,"skill-same:01M792SWFH9R3XRW6VFD8F7GVM":58},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":35,"commitSha":36,"skillDirectory":37,"forksCount":38,"repositoryHtmlUrl":39,"repositoryHomepageUrl":6,"ownerHtmlUrl":40,"ownerBlogUrl":41,"ownerDescription":42,"updatedAt":43,"install":44,"resources":57},"01M792SWFH9R3XRW6VFD8F7GVM","linear-security-basics",null,"Secure API key management, OAuth best practices, and webhook\nverification for Linear integrations.\nTrigger: \"linear security\", \"linear API key security\",\n\"linear OAuth\", \"secure linear\", \"linear webhook verification\",\n\"linear secrets management\", \"linear token refresh\".","jukeyman\u002Fjukeyman-skills","jukeyman","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F142960420?v=4","01M78FR4Y3DHMMCBCC0GXXNB3F","RICK JEFFERSON","01M78FR4Z8RVQRAD31Y5J7V6EB",1,"MIT","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fsaas-packs--linear-pack--linear-security-basics\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: linear-security-basics\ndescription: |\n  Secure API key management, OAuth best practices, and webhook\n  verification for Linear integrations.\n  Trigger: \"linear security\", \"linear API key security\",\n  \"linear OAuth\", \"secure linear\", \"linear webhook verification\",\n  \"linear secrets management\", \"linear token refresh\".\nallowed-tools: Read, Write, Edit, Grep\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ncompatible-with: claude-code, codex, openclaw\ntags: [saas, linear, api, security, authentication]\n---\n# Linear Security Basics\n\n## Overview\nSecure authentication patterns for Linear integrations: API key management, OAuth 2.0 with PKCE, token refresh (mandatory for new apps after Oct 2025), webhook HMAC-SHA256 signature verification, and secret rotation.\n\n## Prerequisites\n- Linear account with API access\n- Understanding of environment variables and secret management\n- Familiarity with OAuth 2.0 and HMAC concepts\n\n## Instructions\n\n### Step 1: Secure API Key Storage\n```typescript\n\u002F\u002F NEVER hardcode keys\n\u002F\u002F BAD:\n\u002F\u002F const client = new LinearClient({ apiKey: \"lin_api_xxxx\" });\n\n\u002F\u002F GOOD: environment variable\nimport { LinearClient } from \"@linear\u002Fsdk\";\n\nconst client = new LinearClient({\n  apiKey: process.env.LINEAR_API_KEY!,\n});\n```\n\n**Environment setup:**\n```bash\n# .env (never commit)\nLINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxx\nLINEAR_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx\n\n# .gitignore\n.env\n.env.*\n!.env.example\n\n# .env.example (commit for documentation)\nLINEAR_API_KEY=lin_api_your_key_here\nLINEAR_WEBHOOK_SECRET=your_webhook_secret_here\n```\n\n**Startup validation:**\n```typescript\nfunction validateConfig(): void {\n  const key = process.env.LINEAR_API_KEY;\n  if (!key) throw new Error(\"LINEAR_API_KEY is required\");\n  if (!key.startsWith(\"lin_api_\")) throw new Error(\"LINEAR_API_KEY has invalid format\");\n  if (key.length \u003C 30) throw new Error(\"LINEAR_API_KEY appears truncated\");\n}\nvalidateConfig();\n```\n\n### Step 2: OAuth 2.0 with PKCE\n```typescript\nimport express from \"express\";\nimport crypto from \"crypto\";\n\nconst app = express();\n\nconst OAUTH = {\n  clientId: process.env.LINEAR_CLIENT_ID!,\n  clientSecret: process.env.LINEAR_CLIENT_SECRET!,\n  redirectUri: process.env.LINEAR_REDIRECT_URI!,\n  scopes: [\"read\", \"write\", \"issues:create\"],\n};\n\n\u002F\u002F Generate PKCE verifier and challenge\nfunction generatePKCE() {\n  const verifier = crypto.randomBytes(32).toString(\"base64url\");\n  const challenge = crypto.createHash(\"sha256\").update(verifier).digest(\"base64url\");\n  return { verifier, challenge };\n}\n\n\u002F\u002F Step 1: Redirect to Linear authorization\napp.get(\"\u002Fauth\u002Flinear\", (req, res) => {\n  const state = crypto.randomBytes(16).toString(\"hex\");\n  const { verifier, challenge } = generatePKCE();\n\n  \u002F\u002F Store state + verifier in session\n  req.session!.oauthState = state;\n  req.session!.codeVerifier = verifier;\n\n  const url = new URL(\"https:\u002F\u002Flinear.app\u002Foauth\u002Fauthorize\");\n  url.searchParams.set(\"client_id\", OAUTH.clientId);\n  url.searchParams.set(\"redirect_uri\", OAUTH.redirectUri);\n  url.searchParams.set(\"response_type\", \"code\");\n  url.searchParams.set(\"scope\", OAUTH.scopes.join(\",\"));\n  url.searchParams.set(\"state\", state);\n  url.searchParams.set(\"code_challenge\", challenge);\n  url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n  res.redirect(url.toString());\n});\n\n\u002F\u002F Step 2: Handle callback — exchange code for tokens\napp.get(\"\u002Fauth\u002Flinear\u002Fcallback\", async (req, res) => {\n  const { code, state } = req.query;\n\n  \u002F\u002F CSRF check\n  if (state !== req.session!.oauthState) {\n    return res.status(400).json({ error: \"Invalid state parameter\" });\n  }\n\n  const response = await fetch(\"https:\u002F\u002Fapi.linear.app\u002Foauth\u002Ftoken\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application\u002Fx-www-form-urlencoded\" },\n    body: new URLSearchParams({\n      grant_type: \"authorization_code\",\n      code: code as string,\n      client_id: OAUTH.clientId,\n      client_secret: OAUTH.clientSecret,\n      redirect_uri: OAUTH.redirectUri,\n      code_verifier: req.session!.codeVerifier,\n    }),\n  });\n\n  const tokens = await response.json();\n\n  \u002F\u002F Store tokens securely (encrypt at rest)\n  await storeTokens(req.user!.id, {\n    accessToken: encrypt(tokens.access_token),\n    refreshToken: encrypt(tokens.refresh_token),\n    expiresAt: new Date(Date.now() + tokens.expires_in * 1000),\n  });\n\n  res.redirect(\"\u002Fdashboard\");\n});\n```\n\n### Step 3: Token Refresh\nAs of Oct 2025, all new Linear OAuth apps issue refresh tokens. Existing apps must migrate by April 2026.\n\n```typescript\nasync function getValidToken(userId: string): Promise\u003Cstring> {\n  const stored = await getStoredTokens(userId);\n\n  \u002F\u002F Refresh if expired or expiring within 5 minutes\n  if (stored.expiresAt.getTime() - Date.now() \u003C 5 * 60 * 1000) {\n    const response = await fetch(\"https:\u002F\u002Fapi.linear.app\u002Foauth\u002Ftoken\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application\u002Fx-www-form-urlencoded\" },\n      body: new URLSearchParams({\n        grant_type: \"refresh_token\",\n        refresh_token: decrypt(stored.refreshToken),\n        client_id: process.env.LINEAR_CLIENT_ID!,\n        client_secret: process.env.LINEAR_CLIENT_SECRET!,\n      }),\n    });\n\n    if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);\n    const tokens = await response.json();\n\n    \u002F\u002F Linear rotates refresh tokens — always store the new one\n    await storeTokens(userId, {\n      accessToken: encrypt(tokens.access_token),\n      refreshToken: encrypt(tokens.refresh_token),\n      expiresAt: new Date(Date.now() + tokens.expires_in * 1000),\n    });\n\n    return tokens.access_token;\n  }\n\n  return decrypt(stored.accessToken);\n}\n```\n\n### Step 4: Webhook Signature Verification\nLinear signs every webhook with HMAC-SHA256 using the webhook's signing secret. The signature is in the `Linear-Signature` header.\n\n```typescript\nimport crypto from \"crypto\";\n\nfunction verifyWebhookSignature(\n  rawBody: string,\n  signature: string,\n  secret: string\n): boolean {\n  const expected = crypto\n    .createHmac(\"sha256\", secret)\n    .update(rawBody)\n    .digest(\"hex\");\n\n  \u002F\u002F Timing-safe comparison to prevent timing attacks\n  try {\n    return crypto.timingSafeEqual(\n      Buffer.from(signature),\n      Buffer.from(expected)\n    );\n  } catch {\n    return false; \u002F\u002F Length mismatch\n  }\n}\n\n\u002F\u002F Express middleware — must use raw body parser\napp.post(\"\u002Fwebhooks\u002Flinear\", express.raw({ type: \"*\u002F*\" }), (req, res) => {\n  const signature = req.headers[\"linear-signature\"] as string;\n  const rawBody = req.body.toString();\n\n  if (!verifyWebhookSignature(rawBody, signature, process.env.LINEAR_WEBHOOK_SECRET!)) {\n    return res.status(401).json({ error: \"Invalid signature\" });\n  }\n\n  \u002F\u002F Verify webhook timestamp (guard against replay attacks)\n  const event = JSON.parse(rawBody);\n  const age = Date.now() - event.webhookTimestamp;\n  if (age > 60000) {\n    return res.status(400).json({ error: \"Webhook too old\" });\n  }\n\n  processEvent(event).catch(console.error);\n  res.json({ received: true });\n});\n```\n\n### Step 5: Secret Rotation\n```typescript\n\u002F\u002F Support multiple keys during rotation period\nconst apiKeys = [\n  process.env.LINEAR_API_KEY_NEW,\n  process.env.LINEAR_API_KEY_OLD,\n].filter(Boolean) as string[];\n\nasync function getWorkingClient(): Promise\u003CLinearClient> {\n  for (const apiKey of apiKeys) {\n    try {\n      const client = new LinearClient({ apiKey });\n      await client.viewer; \u002F\u002F Verify key works\n      return client;\n    } catch {\n      continue;\n    }\n  }\n  throw new Error(\"No valid Linear API key found\");\n}\n```\n\n## Security Checklist\n- [ ] API keys in environment variables only (never in code)\n- [ ] `.env` files in `.gitignore`\n- [ ] OAuth state parameter validated (CSRF protection)\n- [ ] PKCE used for public\u002Fmobile clients\n- [ ] Tokens encrypted at rest in database\n- [ ] Token refresh implemented (mandatory for new apps)\n- [ ] Webhook signatures verified with `crypto.timingSafeEqual`\n- [ ] Webhook timestamp checked (\u003C 60s age)\n- [ ] HTTPS enforced on all endpoints\n- [ ] Minimal OAuth scopes requested\n- [ ] API keys rotated quarterly\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Invalid signature` | Webhook secret mismatch | Verify `LINEAR_WEBHOOK_SECRET` in Linear Settings > API > Webhooks |\n| `invalid_grant` | Refresh token expired\u002Frevoked | Re-initiate full OAuth flow |\n| `Invalid scope` | App not authorized for scope | Request only scopes your app needs |\n| `Authentication required` | Token expired, refresh failed | Trigger re-authentication |\n\n## Examples\n\n### Test Webhook Signature Locally\n```typescript\nimport crypto from \"crypto\";\n\nconst secret = \"test-signing-secret\";\nconst payload = JSON.stringify({\n  action: \"create\",\n  type: \"Issue\",\n  data: { id: \"test\", title: \"Test\" },\n  webhookTimestamp: Date.now(),\n});\nconst sig = crypto.createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\nconsole.log(`Signature: ${sig}`);\nconsole.log(`Valid: ${verifyWebhookSignature(payload, sig, secret)}`);\n```\n\n## Resources\n- [OAuth 2.0 Authentication](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Foauth-2-0-authentication)\n- [OAuth Actor Authorization](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Foauth-actor-authorization)\n- [Webhooks Documentation](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Fwebhooks)\n- [API Settings](https:\u002F\u002Flinear.app\u002Fsettings\u002Fapi)\n","# Linear Security Basics\n\n## Overview\nSecure authentication patterns for Linear integrations: API key management, OAuth 2.0 with PKCE, token refresh (mandatory for new apps after Oct 2025), webhook HMAC-SHA256 signature verification, and secret rotation.\n\n## Prerequisites\n- Linear account with API access\n- Understanding of environment variables and secret management\n- Familiarity with OAuth 2.0 and HMAC concepts\n\n## Instructions\n\n### Step 1: Secure API Key Storage\n```typescript\n\u002F\u002F NEVER hardcode keys\n\u002F\u002F BAD:\n\u002F\u002F const client = new LinearClient({ apiKey: \"lin_api_xxxx\" });\n\n\u002F\u002F GOOD: environment variable\nimport { LinearClient } from \"@linear\u002Fsdk\";\n\nconst client = new LinearClient({\n  apiKey: process.env.LINEAR_API_KEY!,\n});\n```\n\n**Environment setup:**\n```bash\n# .env (never commit)\nLINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxx\nLINEAR_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx\n\n# .gitignore\n.env\n.env.*\n!.env.example\n\n# .env.example (commit for documentation)\nLINEAR_API_KEY=lin_api_your_key_here\nLINEAR_WEBHOOK_SECRET=your_webhook_secret_here\n```\n\n**Startup validation:**\n```typescript\nfunction validateConfig(): void {\n  const key = process.env.LINEAR_API_KEY;\n  if (!key) throw new Error(\"LINEAR_API_KEY is required\");\n  if (!key.startsWith(\"lin_api_\")) throw new Error(\"LINEAR_API_KEY has invalid format\");\n  if (key.length \u003C 30) throw new Error(\"LINEAR_API_KEY appears truncated\");\n}\nvalidateConfig();\n```\n\n### Step 2: OAuth 2.0 with PKCE\n```typescript\nimport express from \"express\";\nimport crypto from \"crypto\";\n\nconst app = express();\n\nconst OAUTH = {\n  clientId: process.env.LINEAR_CLIENT_ID!,\n  clientSecret: process.env.LINEAR_CLIENT_SECRET!,\n  redirectUri: process.env.LINEAR_REDIRECT_URI!,\n  scopes: [\"read\", \"write\", \"issues:create\"],\n};\n\n\u002F\u002F Generate PKCE verifier and challenge\nfunction generatePKCE() {\n  const verifier = crypto.randomBytes(32).toString(\"base64url\");\n  const challenge = crypto.createHash(\"sha256\").update(verifier).digest(\"base64url\");\n  return { verifier, challenge };\n}\n\n\u002F\u002F Step 1: Redirect to Linear authorization\napp.get(\"\u002Fauth\u002Flinear\", (req, res) => {\n  const state = crypto.randomBytes(16).toString(\"hex\");\n  const { verifier, challenge } = generatePKCE();\n\n  \u002F\u002F Store state + verifier in session\n  req.session!.oauthState = state;\n  req.session!.codeVerifier = verifier;\n\n  const url = new URL(\"https:\u002F\u002Flinear.app\u002Foauth\u002Fauthorize\");\n  url.searchParams.set(\"client_id\", OAUTH.clientId);\n  url.searchParams.set(\"redirect_uri\", OAUTH.redirectUri);\n  url.searchParams.set(\"response_type\", \"code\");\n  url.searchParams.set(\"scope\", OAUTH.scopes.join(\",\"));\n  url.searchParams.set(\"state\", state);\n  url.searchParams.set(\"code_challenge\", challenge);\n  url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n  res.redirect(url.toString());\n});\n\n\u002F\u002F Step 2: Handle callback — exchange code for tokens\napp.get(\"\u002Fauth\u002Flinear\u002Fcallback\", async (req, res) => {\n  const { code, state } = req.query;\n\n  \u002F\u002F CSRF check\n  if (state !== req.session!.oauthState) {\n    return res.status(400).json({ error: \"Invalid state parameter\" });\n  }\n\n  const response = await fetch(\"https:\u002F\u002Fapi.linear.app\u002Foauth\u002Ftoken\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application\u002Fx-www-form-urlencoded\" },\n    body: new URLSearchParams({\n      grant_type: \"authorization_code\",\n      code: code as string,\n      client_id: OAUTH.clientId,\n      client_secret: OAUTH.clientSecret,\n      redirect_uri: OAUTH.redirectUri,\n      code_verifier: req.session!.codeVerifier,\n    }),\n  });\n\n  const tokens = await response.json();\n\n  \u002F\u002F Store tokens securely (encrypt at rest)\n  await storeTokens(req.user!.id, {\n    accessToken: encrypt(tokens.access_token),\n    refreshToken: encrypt(tokens.refresh_token),\n    expiresAt: new Date(Date.now() + tokens.expires_in * 1000),\n  });\n\n  res.redirect(\"\u002Fdashboard\");\n});\n```\n\n### Step 3: Token Refresh\nAs of Oct 2025, all new Linear OAuth apps issue refresh tokens. Existing apps must migrate by April 2026.\n\n```typescript\nasync function getValidToken(userId: string): Promise\u003Cstring> {\n  const stored = await getStoredTokens(userId);\n\n  \u002F\u002F Refresh if expired or expiring within 5 minutes\n  if (stored.expiresAt.getTime() - Date.now() \u003C 5 * 60 * 1000) {\n    const response = await fetch(\"https:\u002F\u002Fapi.linear.app\u002Foauth\u002Ftoken\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application\u002Fx-www-form-urlencoded\" },\n      body: new URLSearchParams({\n        grant_type: \"refresh_token\",\n        refresh_token: decrypt(stored.refreshToken),\n        client_id: process.env.LINEAR_CLIENT_ID!,\n        client_secret: process.env.LINEAR_CLIENT_SECRET!,\n      }),\n    });\n\n    if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);\n    const tokens = await response.json();\n\n    \u002F\u002F Linear rotates refresh tokens — always store the new one\n    await storeTokens(userId, {\n      accessToken: encrypt(tokens.access_token),\n      refreshToken: encrypt(tokens.refresh_token),\n      expiresAt: new Date(Date.now() + tokens.expires_in * 1000),\n    });\n\n    return tokens.access_token;\n  }\n\n  return decrypt(stored.accessToken);\n}\n```\n\n### Step 4: Webhook Signature Verification\nLinear signs every webhook with HMAC-SHA256 using the webhook's signing secret. The signature is in the `Linear-Signature` header.\n\n```typescript\nimport crypto from \"crypto\";\n\nfunction verifyWebhookSignature(\n  rawBody: string,\n  signature: string,\n  secret: string\n): boolean {\n  const expected = crypto\n    .createHmac(\"sha256\", secret)\n    .update(rawBody)\n    .digest(\"hex\");\n\n  \u002F\u002F Timing-safe comparison to prevent timing attacks\n  try {\n    return crypto.timingSafeEqual(\n      Buffer.from(signature),\n      Buffer.from(expected)\n    );\n  } catch {\n    return false; \u002F\u002F Length mismatch\n  }\n}\n\n\u002F\u002F Express middleware — must use raw body parser\napp.post(\"\u002Fwebhooks\u002Flinear\", express.raw({ type: \"*\u002F*\" }), (req, res) => {\n  const signature = req.headers[\"linear-signature\"] as string;\n  const rawBody = req.body.toString();\n\n  if (!verifyWebhookSignature(rawBody, signature, process.env.LINEAR_WEBHOOK_SECRET!)) {\n    return res.status(401).json({ error: \"Invalid signature\" });\n  }\n\n  \u002F\u002F Verify webhook timestamp (guard against replay attacks)\n  const event = JSON.parse(rawBody);\n  const age = Date.now() - event.webhookTimestamp;\n  if (age > 60000) {\n    return res.status(400).json({ error: \"Webhook too old\" });\n  }\n\n  processEvent(event).catch(console.error);\n  res.json({ received: true });\n});\n```\n\n### Step 5: Secret Rotation\n```typescript\n\u002F\u002F Support multiple keys during rotation period\nconst apiKeys = [\n  process.env.LINEAR_API_KEY_NEW,\n  process.env.LINEAR_API_KEY_OLD,\n].filter(Boolean) as string[];\n\nasync function getWorkingClient(): Promise\u003CLinearClient> {\n  for (const apiKey of apiKeys) {\n    try {\n      const client = new LinearClient({ apiKey });\n      await client.viewer; \u002F\u002F Verify key works\n      return client;\n    } catch {\n      continue;\n    }\n  }\n  throw new Error(\"No valid Linear API key found\");\n}\n```\n\n## Security Checklist\n- [ ] API keys in environment variables only (never in code)\n- [ ] `.env` files in `.gitignore`\n- [ ] OAuth state parameter validated (CSRF protection)\n- [ ] PKCE used for public\u002Fmobile clients\n- [ ] Tokens encrypted at rest in database\n- [ ] Token refresh implemented (mandatory for new apps)\n- [ ] Webhook signatures verified with `crypto.timingSafeEqual`\n- [ ] Webhook timestamp checked (\u003C 60s age)\n- [ ] HTTPS enforced on all endpoints\n- [ ] Minimal OAuth scopes requested\n- [ ] API keys rotated quarterly\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Invalid signature` | Webhook secret mismatch | Verify `LINEAR_WEBHOOK_SECRET` in Linear Settings > API > Webhooks |\n| `invalid_grant` | Refresh token expired\u002Frevoked | Re-initiate full OAuth flow |\n| `Invalid scope` | App not authorized for scope | Request only scopes your app needs |\n| `Authentication required` | Token expired, refresh failed | Trigger re-authentication |\n\n## Examples\n\n### Test Webhook Signature Locally\n```typescript\nimport crypto from \"crypto\";\n\nconst secret = \"test-signing-secret\";\nconst payload = JSON.stringify({\n  action: \"create\",\n  type: \"Issue\",\n  data: { id: \"test\", title: \"Test\" },\n  webhookTimestamp: Date.now(),\n});\nconst sig = crypto.createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\nconsole.log(`Signature: ${sig}`);\nconsole.log(`Valid: ${verifyWebhookSignature(payload, sig, secret)}`);\n```\n\n## Resources\n- [OAuth 2.0 Authentication](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Foauth-2-0-authentication)\n- [OAuth Actor Authorization](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Foauth-actor-authorization)\n- [Webhooks Documentation](https:\u002F\u002Flinear.app\u002Fdevelopers\u002Fwebhooks)\n- [API Settings](https:\u002F\u002Flinear.app\u002Fsettings\u002Fapi)\n",{"name":5,"tags":24,"author":30,"license":15,"version":31,"description":32,"allowed-tools":33,"compatible-with":34},[25,26,27,28,29],"saas","linear","api","security","authentication","Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Secure API key management, OAuth best practices, and webhook\nverification for Linear integrations.\nTrigger: \"linear security\", \"linear API key security\",\n\"linear OAuth\", \"secure linear\", \"linear webhook verification\",\n\"linear secrets management\", \"linear token refresh\".\n","Read, Write, Edit, Grep","claude-code, codex, openclaw","main","99825ad3f3bcc6597ddcc7294a14e6e922f2b67b","skills\u002Fsaas-packs--linear-pack--linear-security-basics",0,"https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills","https:\u002F\u002Fgithub.com\u002Fjukeyman","https:\u002F\u002FRJBUSINESSSOLUTIONS.ORG","Credit and Finance Expert | Developer\r\n\r\nI'm Rick Jefferson, a seasoned credit and finance expert with over a decade of experience in financial literacy,","2026-07-13 07:03:29",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add jukeyman\u002Fjukeyman-skills --skill linear-security-basics",{"tool":50,"command":51},"bunx","bunx skills add jukeyman\u002Fjukeyman-skills --skill linear-security-basics",{"tool":53,"command":54},"pnpm","pnpm dlx skills add jukeyman\u002Fjukeyman-skills --skill linear-security-basics","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fsaas-packs--linear-pack--linear-security-basics\u002FSKILL.md",true,[],[59,66,73,80,87,95],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":65,"publishedAt":19},"01M79ANS2SM329NEP5ZCX106RR","zustand-store-creator","Create zustand store creator operations. Auto-activating skill for Frontend Development.\nTriggers on: zustand store creator, zustand store creator\nPart of the Frontend Development skill category. Use when working with zustand store creator functionality. Trigger with phrases like \"zustand store creator\", \"zustand creator\", \"zustand\".","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fzustand-store-creator\u002FSKILL.md",[],[],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":19},"01M79AMZKP4YDEZ7EHG1Y7PP74","zero-trust-config-helper","Configure with zero trust config helper operations. Auto-activating skill for Security Advanced.\nTriggers on: zero trust config helper, zero trust config helper\nPart of the Security Advanced skill category. Use when configuring systems or services. Trigger with phrases like \"zero trust config helper\", \"zero helper\", \"zero\".","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fzero-trust-config-helper\u002FSKILL.md",[],[],{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":19},"01M79AMJSBCEJQNBTYNAHYN5HM","zapier-integration-helper","Assist with zapier integration helper operations. Auto-activating skill for Business Automation.\nTriggers on: zapier integration helper, zapier integration helper\nPart of the Business Automation skill category. Use when working with APIs or building integrations. Trigger with phrases like \"zapier integration helper\", \"zapier helper\", \"zapier\".","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fzapier-integration-helper\u002FSKILL.md",[],[],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":19},"01M79AHJZGVMARS13VFQYRR1MP","yaml-config-validator","Validate yaml config validator operations. Auto-activating skill for DevOps Basics.\nTriggers on: yaml config validator, yaml config validator\nPart of the DevOps Basics skill category. Use when configuring systems or services. Trigger with phrases like \"yaml config validator\", \"yaml validator\", \"yaml\".","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fyaml-config-validator\u002FSKILL.md",[],[],{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":91,"sourceHtmlUrl":92,"categories":93,"keywords":94,"publishedAt":19},"01M79AHGDZRN0020RDQSQDPAM8","xvary-stock-research","Thesis-driven equity analysis from public SEC EDGAR and market data; \u002Fanalyze, \u002Fscore, \u002Fcompare workflows with bundled Python tools (Claude Code, Cursor, Codex).","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fxvary-stock-research\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fxvary-stock-research\u002FSKILL.md",[],[],{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":19},"01M79AHCYJ9FV9RMHQ24HDRN33","xss-vulnerability-scanner","Scan xss vulnerability scanner operations. Auto-activating skill for Security Fundamentals.\nTriggers on: xss vulnerability scanner, xss vulnerability scanner\nPart of the Security Fundamentals skill category. Use when working with xss vulnerability scanner functionality. Trigger with phrases like \"xss vulnerability scanner\", \"xss scanner\", \"xss\".","https:\u002F\u002Fgithub.com\u002Fjukeyman\u002Fjukeyman-skills\u002Fblob\u002Fmain\u002Fskills\u002Fxss-vulnerability-scanner\u002FSKILL.md",[],[]]