@netlify/agent-runner-cli 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: netlify-access-control
3
+ description: 'Picks the right Netlify site-protection layer and disambiguates the three unrelated "auth" concepts users conflate — app-user login (Netlify Identity), site-load gating (Password Protection / project visibility), and dashboard SAML SSO. Use it when asked to password-protect a site or Deploy Preview, make a project private/public, restrict a site to your team, require SSO to view a site, set up company-wide app SSO, or invite users to a private project. Also use it for SSO-session symptoms on protected sites: "logged out mid-session", 401s after about an hour, or token expiry/refresh questions. Not for wiring auth code — route app-login setup to netlify-identity.'
4
+ ---
5
+
6
+ # Netlify access control — pick the protection layer
7
+
8
+ This skill routes you to the correct protection layer. It does not teach each one. **These settings have no public API, CLI command, or MCP tool.** Never curl `api.netlify.com` or read local auth tokens to inspect or change them — give the user the dashboard path and checklist. On failure, report what you tried and stop.
9
+
10
+ ## First: disambiguate "auth" — three unrelated layers
11
+
12
+ Users constantly conflate these. Identify which one is meant before recommending anything.
13
+
14
+ 1. **Netlify Identity** — "who is this user *inside my app*." Issues `nf_jwt`. → route to the **netlify-identity** skill; not covered here.
15
+ 2. **Password Protection / project visibility** — "can this request load the site at all." Covered here.
16
+ 3. **Team/Org SAML SSO** — "can you log in to the Netlify *dashboard*." Gates dashboard access; also underlies team-login site protection.
17
+
18
+ Sessions are separate. The same provider (e.g. Google) can appear twice unrelated — Identity OAuth for app users vs. SAML IdP for team members.
19
+
20
+ **Double-login footgun:** a Password-Protection/team-login perimeter session and an Identity app session have **no bridge** — no shared cookie, no header forwarding, no JWT exchange. Don't try to wire them together. For the combined layered pattern and its tradeoffs, see `references/two-layer-pattern.md`.
21
+
22
+ **Want company-wide app SSO with a single sign-in (no double login)?** Recommend the **Auth0 extension** federating to the corporate IdP *before* the two-layer stack.
23
+
24
+ ## Decision guide (this skill's job)
25
+
26
+ - Restrict entire site to your team, invite by email → **Private project** (Credit-based) or **Team login protection** (Password Protection).
27
+ - Share with anyone holding one shared password → **Basic password protection** (Pro) or **Password** visibility (Pro, Credit-based).
28
+ - Keep production public, protect previews only → scope **Previews only** / **Non-production deploys only**.
29
+ - Require SSO to *view a site* → Organization/Team SSO with **Only SSO allowed (strict)**, then Password Protection with **Team login protection**.
30
+ - Protect specific pages/sections with multiple passwords → **Basic authentication with custom HTTP headers** (formerly Selective password protection): https://docs.netlify.com/manage/security/secure-access-to-sites/basic-authentication-with-custom-http-headers/
31
+ - Authenticate your own end users → **Netlify Identity** / **OAuth provider tokens** / **Role-based access control with JWT** → route to netlify-identity.
32
+ - Block malicious/automated traffic or AI crawlers → **Advanced Web Security** (WAF / Firewall Traffic Rules / rate limiting) or **User Agent Blocker** extension: https://docs.netlify.com/build/build-with-ai/block-ai-crawlers/
33
+
34
+ ## Key distinction: Private vs Password
35
+
36
+ - **Private** already requires Netlify credentials — no shared password. Invite by email; recommended for team-only access.
37
+ - **Password** = one universal shared password anyone can use (including managing team members, who must also enter it). No SSO.
38
+ - **Team login protection** = same mechanism as Private: a visitor must log in as a member of your Netlify team, and **Reviewers** you invite can get in too (unlimited and not counted toward the member count on legacy plans; Pro or higher on Credit-based plans). **Git Contributors cannot log in** — invite them as Reviewers rather than upgrading them to Developer.
39
+
40
+ ## SSO-session symptom: 401s after ~1 hour
41
+
42
+ If a user reports being "logged out mid-session" or 401s on an SSO-protected site: **SSO auth tokens expire after 1 hour**, after which requests return `401`. Sites with SSO protection return the header **`Netlify-Site-Protection-Expires-In`** — seconds until the request's token expires. Refresh proactively:
43
+
44
+ ```js
45
+ // Client-side. Checks the Netlify SSO protection header and reloads before expiry.
46
+ const res = await fetch(window.location.href, { method: "HEAD" });
47
+ const secondsLeft = Number(res.headers.get("Netlify-Site-Protection-Expires-In"));
48
+ // Tokens last 1 hour (3600s). Reload a bit early to avoid a 401.
49
+ if (!Number.isNaN(secondsLeft) && secondsLeft < 60) {
50
+ window.location.reload();
51
+ }
52
+ ```
53
+
54
+ ## UI paths (the only path — no API)
55
+
56
+ **Credit-based plans (Free, Personal, Pro)** — project-level "Password Protection" is replaced by **Project visibility**:
57
+ - Per project: Project configuration > General > Visitor access > **Project visibility** — `https://app.netlify.com/projects/{site_name}/configuration/general/#project-visibility`. Edit visibility → (Customize if a team default exists) → **Public** / **Password** (Pro only) / **Private** → set **Preview access** (Production and previews / Previews only) → Save.
58
+ - Team default: Team settings > General > Visitor access > **Default project visibility** — `https://app.netlify.com/teams/{team_name}/settings/general#default-project-visibility`. Options: Private for new projects / Private for all projects / Public for new projects.
59
+ - No per-team default *password* here; set a password per project.
60
+
61
+ **Enterprise / Open Source / legacy (non-Credit-based)** — use **Password Protection** UI:
62
+ - Per site: Project configuration > General > Visitor access > **Password Protection** — `https://app.netlify.com/projects/{site_name}/configuration/general#visitor-access`. Configure → Basic or Team login → scope (All deploys / Non-production deploys only) → Save.
63
+ - Team default: Team settings > Access & security > Visitor access > **Default Password Protection settings** — `https://app.netlify.com/teams/{team_name}/settings/access#default-site-protection-settings`. Applies to all sites without their own settings.
64
+
65
+ **Legacy → Credit-based mapping:** No protection→Public · Basic protection→Password · Team protection→Private · All deploys→Production and previews · Non-production deploys only→Previews only.
66
+
67
+ ## Constraints & footguns
68
+
69
+ - **Site-specific Password Protection overrides team defaults.**
70
+ - **Who can change these settings:** project visibility — Organization Owners (on certain Enterprise plans), Team Owners, and Developers with access to that project; **Internal Builders cannot publish to production, so they cannot make a project public**. Password Protection — a Developer changes it per site, a Team Owner sets the team default.
71
+ - **Advanced Web Security runs before password/login prompts** — a blocked IP hits an error page before ever seeing the prompt. Internal order: Firewall Traffic Rules → WAF → Rate limiting.
72
+ - **Third-party webhooks (Slack, Stripe, etc.) cannot reach a private project** — receiving webhooks requires the project to be **public**.
73
+ - **Make public** requires at least one successful **production deploy**.
74
+ - **Protecting only non-production deploys** with Password Protection is **Enterprise only**.
75
+ - **Plan gating:** Basic password protection for the whole site → all Pro plans; all Password Protection options → Enterprise. Project visibility (public/private, private-by-default) → Credit-based Free/Personal/Pro only; password-protected visibility → Pro only. On Free/Personal a private project is visible only to the Team Owner (single-seat); Pro allows unlimited members.
76
+ - **Team default changes by creation date:** teams created on/after **July 28, 2026** default to **Private for new projects**; earlier teams default to **Public**.
77
+ - **Renamed:** "site-wide password protection" (old name of a Password Protection option); "Selective password protection" → Basic authentication with custom HTTP headers.
78
+
79
+ Reference: https://docs.netlify.com/manage/security/secure-access-to-sites/overview/ · https://docs.netlify.com/manage/security/secure-access-to-sites/password-protection/ · https://docs.netlify.com/manage/security/secure-access-to-sites/project-visibility/
80
+
81
+ <!-- Advanced Web Security (WAF, Firewall Traffic Rules, rate limiting) specifics — limits, config keys, plan gating — not in source; referenced by URL only. -->
82
+ <!-- Exact per-tier matrix of basic vs team-login options across plans is only partially stated in sources. -->
83
+
84
+ <!-- system: agent-context/access-control/system.md — human-owned, merged by ctx-gen; edit system.md, not this section -->
85
+ # Netlify house rules (access-control)
86
+
87
+ These are org conventions, not docs facts — merged into the rendered skill by
88
+ ctx-gen and never generated. Owned by the skills maintainer.
89
+
90
+ 1. This is a routing/disambiguation skill: keep it narrow — its job is
91
+ picking the right protection layer, not teaching each one.
92
+ 2. The combined Password-Protection + Identity pattern lives in this skill's
93
+ `references/two-layer-pattern.md`.
94
+ 3. "Auth" on Netlify is three unrelated layers users constantly conflate:
95
+ Netlify Identity ("who is this user inside my app" — issues `nf_jwt`),
96
+ Password Protection / project visibility ("can this request load the site
97
+ at all"), and Team/Org SAML SSO ("can you log in to the Netlify
98
+ dashboard"). Sessions are separate; the same provider (Google) can appear
99
+ in two unrelated places — Identity OAuth for app users, SAML IdP for team
100
+ members. Disambiguate before recommending anything.
101
+ 4. The double login is real: a Password-Protection/team-login perimeter
102
+ session and an Identity app session have no bridge — no shared cookie, no
103
+ header forwarding, no JWT exchange. Don't burn iterations wiring them
104
+ together; tradeoffs live in `references/two-layer-pattern.md`.
105
+ 5. These settings have no public API, CLI command, or MCP tool. Never curl
106
+ `api.netlify.com` or read local auth tokens to inspect or change them —
107
+ hand the user the dashboard path and checklist; on failure, report what
108
+ you tried and stop.
109
+ 6. Identity setup, auth code, and OAuth providers for app users belong to the
110
+ netlify-identity skill — route there; this skill only picks the layer.
111
+ 7. For company-wide app-level SSO with a single sign-in (no double login),
112
+ the Auth0 extension — federating to the corporate IdP — is the
113
+ recommendation before the two-layer stack.
114
+ 8. The description's triggers must include the SSO-session symptoms users
115
+ actually report — "logged out mid-session", 401s on an SSO-protected
116
+ site, token expiry/refresh — not only setup phrasing. The
117
+ `Netlify-Site-Protection-Expires-In` guidance is unreachable if the
118
+ skill never triggers on the symptom.
119
+ 9. Team login protection excludes Git Contributors, and the answer to that is
120
+ **Reviewers**, never a Developer seat. Reviewers can open team-login-
121
+ protected deploys: unlimited and not counted toward the member count on
122
+ legacy plans, Pro or higher on Credit-based plans. Every answer about Git
123
+ Contributor access must name the Reviewer path — recommending an upgrade to
124
+ Developer sells a paid seat per person to solve something the product
125
+ already solves for free. Stating the exclusion without the remedy is the
126
+ failure mode this rule exists to prevent; it has happened. Never frame the
127
+ permitted roles as a closed list ("only X, Y and Z get in") — even with the
128
+ Reviewer path added after it, a closed list reads as Reviewers being shut
129
+ out, and agents repeat it. Naming roles is optional; the question is
130
+ usually only about Git Contributors.
131
+ 10. Say who can change these settings, not only how to change them. Project
132
+ visibility: Organization Owners (on certain Enterprise plans), Team Owners,
133
+ and Developers with access to that project — and Internal Builders cannot
134
+ make a project public, because they cannot publish to production. Password
135
+ Protection: a Developer per site, a Team Owner for the team default. A
136
+ checklist handed to someone without the role is a dead end.
@@ -0,0 +1,69 @@
1
+ # Two-Layer Pattern: Company-Only Access + Per-User Identity
2
+
3
+ A common enterprise ask conflates two concerns:
4
+
5
+ 1. **Perimeter** — the deployed app should be reachable only by company employees.
6
+ 2. **In-app identity** — the app should tell users apart (per-user data, roles).
7
+
8
+ These are different layers (see the main skill). Below are the four ways to satisfy them on Netlify, ordered from least to most friction, plus why the naive "stack both" approach forces a double login.
9
+
10
+ ## Option A — Invite-only Netlify Identity (best default for "just my team")
11
+
12
+ Enable Netlify Identity in **invite-only** registration mode and invite only company addresses. Identity becomes the perimeter *and* the in-app identity: no uninvited user can sign in, and every signed-in user is a real Identity user with `nf_jwt` and roles.
13
+
14
+ - **Plans:** all (free).
15
+ - **Logins:** one.
16
+ - **Per-user identity:** full (roles via `app_metadata`, role-based redirects via `nf_jwt`).
17
+ - **Tradeoffs:** manual invite management; no automatic provisioning from a corporate directory; relies on invite links not being shared. Great for internal tools and small teams; doesn't scale cleanly to thousands of employees.
18
+
19
+ ## Option B — Auth0 extension (best for a real org with an existing IdP)
20
+
21
+ Use the Netlify **Auth0 extension** instead of (or alongside) Identity. Configure Auth0 to federate with the company's IdP (Okta, Entra, Google Workspace, etc.). Auth0 enforces "company-only" via its connection/organization settings and supplies per-user identity — in a **single** sign-in.
22
+
23
+ - **Plans:** Auth0 extension (enterprise-oriented); more setup than Identity.
24
+ - **Logins:** one (federated SSO).
25
+ - **Per-user identity:** full, from the corporate directory; supports auto-provisioning.
26
+ - **Tradeoffs:** more moving parts than Identity; requires Auth0 configuration. This is the path when invite-only Identity won't scale. Configured via the Netlify Auth0 extension — see the Netlify docs setup guide for Auth0.
27
+
28
+ ## Option C — Basic Password Protection + Netlify Identity
29
+
30
+ Put a shared-password gate in front of the site (Pro+) and run Identity inside for per-user accounts. The password keeps the public out; Identity differentiates users.
31
+
32
+ - **Plans:** Pro+ for the password gate.
33
+ - **Logins:** two (shared password, then Identity) — but the first is a single shared secret, not a per-user login, so it's lighter than Option D.
34
+ - **Use when:** "keep the public out while we build" plus real user accounts, without an Enterprise plan.
35
+ - **Tradeoffs:** the shared password is not per-user and is easily forwarded; it's a soft gate, not real access control.
36
+
37
+ ## Option D — Enterprise team-login perimeter + Netlify Identity (the genuine two-layer, double login)
38
+
39
+ Enable Team/Org SAML SSO and Password Protection with **team login** (optionally "Only SSO allowed (strict)"), then run Netlify Identity separately for app sessions.
40
+
41
+ - **Plans:** Enterprise.
42
+ - **Logins:** **two** — the CDN-edge perimeter (company SSO) *and* the app's Identity login.
43
+ - **Per-user identity:** full, in-app via Identity.
44
+ - **Tradeoffs:** the double login (below) and a hidden seat cost — **team login admits only Netlify team members, so every employee who passes it needs a paid Netlify seat.** Choose this only when a true CDN-edge perimeter is a hard requirement and the double login is acceptable.
45
+
46
+ ## Why the double login can't be wired away (today)
47
+
48
+ When the perimeter gate and Identity are both active, the two sessions are independent:
49
+
50
+ - The perimeter (Password Protection / SAML SSO) authenticates a **Netlify team member** and issues its own session (SSO tokens via this path expire after ~1 hour).
51
+ - Netlify Identity authenticates an **app end user** and issues the `nf_jwt` cookie.
52
+
53
+ There is **no documented passthrough** — no shared cookie, no forwarded header, no JWT exchange — that turns "passed the perimeter" into "logged in to the app." A perimeter session also represents *team-member* identity, not an app *end-user* record with `app_metadata.roles`, so even a hypothetical bridge wouldn't cleanly become the app's user.
54
+
55
+ > An unofficial `netlify/netlify-plugin-identity-sso` repo once attempted a bridge, but it is unofficial, dormant (last release 2021), and hardcoded for `@netlify.com` emails. Don't build on it.
56
+
57
+ Practical takeaway: if single sign-on matters, pick Option A or B. Don't spend iterations trying to fuse the two layers in Option D — it isn't supported.
58
+
59
+ ## What an agent can and can't see
60
+
61
+ While writing code, an agent **cannot** read whether Identity is enabled, which OAuth providers are configured, or whether Password Protection / SSO is on — none of it is exposed by the Netlify API, CLI, or MCP server; it lives in the dashboard. The one runtime exception is the live Identity provider list:
62
+
63
+ ```typescript
64
+ import { getSettings } from '@netlify/identity'
65
+ // Returns autoconfirm, disableSignup, and providers — read this from the running app
66
+ const settings = await getSettings()
67
+ ```
68
+
69
+ `getSettings()` hits `/.netlify/identity/settings` and works against any origin serving the page — including localhost under `netlify dev`, which proxies to the live service — so it helps the running app render the right buttons. It does not help at authoring time (before the app runs), so ask the user what's already configured rather than guessing, and hand off any dashboard changes with an explicit checklist.