@bffless/skills 1.7.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.
- package/.claude-plugin/marketplace.json +18 -0
- package/LICENSE.md +19 -0
- package/README.md +117 -0
- package/package.json +24 -0
- package/plugins/bffless/.claude-plugin/plugin.json +21 -0
- package/plugins/bffless/skills/authentication/SKILL.md +204 -0
- package/plugins/bffless/skills/authorization/SKILL.md +59 -0
- package/plugins/bffless/skills/bffless/SKILL.md +158 -0
- package/plugins/bffless/skills/cache-and-storage/SKILL.md +151 -0
- package/plugins/bffless/skills/chat/SKILL.md +196 -0
- package/plugins/bffless/skills/pipelines/SKILL.md +162 -0
- package/plugins/bffless/skills/proxy-rules/SKILL.md +110 -0
- package/plugins/bffless/skills/repository/SKILL.md +64 -0
- package/plugins/bffless/skills/share-links/SKILL.md +53 -0
- package/plugins/bffless/skills/traffic-splitting/SKILL.md +63 -0
- package/plugins/bffless/skills/upload-artifact/SKILL.md +195 -0
- package/plugins/bffless/skills/use-bff-state/SKILL.md +174 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bffless-plugins",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "BFFless"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "BFFless platform skills",
|
|
8
|
+
"version": "1.7.0"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "bffless",
|
|
13
|
+
"source": "./plugins/bffless",
|
|
14
|
+
"description": "BFFless platform skills — deployments, pipelines, proxy rules, chat, traffic splitting, and more",
|
|
15
|
+
"category": "platform"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# O'Saasy License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BFFless, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to use,
|
|
7
|
+
copy, modify, and distribute the Software, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
10
|
+
all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
2. The Software is provided "as is", without warranty of any kind, express or
|
|
13
|
+
implied, including but not limited to the warranties of merchantability,
|
|
14
|
+
fitness for a particular purpose and noninfringement.
|
|
15
|
+
|
|
16
|
+
3. In no event shall the authors or copyright holders be liable for any claim,
|
|
17
|
+
damages or other liability, whether in an action of contract, tort or
|
|
18
|
+
otherwise, arising from, out of or in connection with the Software or the
|
|
19
|
+
use or other dealings in the Software.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @bffless/skills
|
|
2
|
+
|
|
3
|
+
Agent skills for [BFFless](https://bffless.app) — a self-hosted static asset hosting platform with AI-powered pipelines.
|
|
4
|
+
|
|
5
|
+
These skills give your AI coding agent domain knowledge about BFFless features so it can help you build, deploy, and configure your projects.
|
|
6
|
+
|
|
7
|
+
Originally built as a Claude Code plugin, the skills are plain markdown and also work with the open-source [`skills`](https://www.npmjs.com/package/skills) CLI — so you can install them into any agent that reads skill files from your project.
|
|
8
|
+
|
|
9
|
+
## Skills Included
|
|
10
|
+
|
|
11
|
+
| Skill | Description |
|
|
12
|
+
| --------------------- | -------------------------------------------------------- |
|
|
13
|
+
| **authentication** | Cross-domain auth, login relay, cookie sessions |
|
|
14
|
+
| **authorization** | Global and project roles, API keys, permission model |
|
|
15
|
+
| **bffless** | Platform overview, key concepts, and feature summary |
|
|
16
|
+
| **cache-and-storage** | Cache rules, storage backends, API keys for CI/CD |
|
|
17
|
+
| **chat** | AI chat widget/full-page, skills, streaming, persistence |
|
|
18
|
+
| **pipelines** | Backend automation with handler chains and DB Records |
|
|
19
|
+
| **proxy-rules** | Forward requests to backend APIs, eliminate CORS |
|
|
20
|
+
| **repository** | Deployments, aliases, content browser, rollback |
|
|
21
|
+
| **share-links** | Token-based sharing for private deployments |
|
|
22
|
+
| **traffic-splitting** | A/B testing, canary deployments, weighted routing |
|
|
23
|
+
| **upload-artifact** | GitHub Action for uploading builds to BFFless |
|
|
24
|
+
| **use-bff-state** | React hook for server-side state with Data Tables |
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
### Claude Code (plugin marketplace)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Add the marketplace
|
|
32
|
+
/plugin marketplace add bffless/skills
|
|
33
|
+
|
|
34
|
+
# Install the plugin
|
|
35
|
+
/plugin install bffless
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or via CLI:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude plugin install bffless --scope user
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Any agent (via `npx skills`)
|
|
45
|
+
|
|
46
|
+
The same skills repo works with the open-source [`skills`](https://www.npmjs.com/package/skills) CLI:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# List available skills
|
|
50
|
+
npx skills add bffless/skills --list
|
|
51
|
+
|
|
52
|
+
# Install all skills into your project
|
|
53
|
+
npx skills add bffless/skills
|
|
54
|
+
|
|
55
|
+
# Install a specific skill
|
|
56
|
+
npx skills add bffless/skills --skill chat
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This clones the repo and copies the selected skill files into your project so any compatible agent can read them.
|
|
60
|
+
|
|
61
|
+
### Local Development
|
|
62
|
+
|
|
63
|
+
To use a local copy with Claude Code (e.g. when contributing):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/bffless/skills.git
|
|
67
|
+
claude --plugin-dir ./skills
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
Once installed, your agent automatically uses these skills when you ask about BFFless features. For example:
|
|
73
|
+
|
|
74
|
+
- "Set up a proxy rule to forward /api requests to my backend"
|
|
75
|
+
- "Add AI chat to my site with streaming"
|
|
76
|
+
- "Configure traffic splitting for a canary deployment"
|
|
77
|
+
- "Set up the upload-artifact GitHub Action for my repo"
|
|
78
|
+
|
|
79
|
+
In Claude Code, skills are invoked by name with the `bffless` namespace:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
/bffless:chat
|
|
83
|
+
/bffless:proxy-rules
|
|
84
|
+
/bffless:pipelines
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Developer Skills vs BFFless Pipeline Skills
|
|
88
|
+
|
|
89
|
+
This package contains **developer-facing agent skills** — they teach your coding agent about the BFFless platform so it can assist you while you build.
|
|
90
|
+
|
|
91
|
+
These are different from **BFFless pipeline skills**, which are markdown files you create in your own project's `.bffless/skills/` directory. Pipeline skills are deployed with your site and loaded by the AI chat handler at runtime to give your chatbot domain-specific knowledge.
|
|
92
|
+
|
|
93
|
+
| | Developer Skills (this package) | Pipeline Skills (your project) |
|
|
94
|
+
| ------------ | --------------------------------------- | --------------------------------------- |
|
|
95
|
+
| **Purpose** | Help your agent help _you_ build on BFFless | Give _your chatbot_ domain knowledge |
|
|
96
|
+
| **Location** | Installed via Claude Code plugin or `npx skills` | `.bffless/skills/` in your project repo |
|
|
97
|
+
| **Used by** | Your coding agent (your dev tool) | BFFless AI chat handler (your users) |
|
|
98
|
+
| **Deployed** | npm / `skills` CLI | `bffless/upload-artifact` GitHub Action |
|
|
99
|
+
|
|
100
|
+
## Documentation
|
|
101
|
+
|
|
102
|
+
- [BFFless Docs](https://docs.bffless.app)
|
|
103
|
+
- [Getting Started](https://docs.bffless.app/getting-started/quickstart)
|
|
104
|
+
- [Skills](https://docs.bffless.app/features/claude-code-plugin)
|
|
105
|
+
- [Chat Feature](https://docs.bffless.app/features/chat/)
|
|
106
|
+
- [Pipelines](https://docs.bffless.app/features/pipelines/)
|
|
107
|
+
|
|
108
|
+
## Contributing
|
|
109
|
+
|
|
110
|
+
1. Fork this repo
|
|
111
|
+
2. Add or update skills in `plugins/bffless/skills/<skill-name>/SKILL.md`
|
|
112
|
+
3. Each skill needs YAML frontmatter with `name` and `description`
|
|
113
|
+
4. Open a PR — CI validates all skills automatically
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
[O'Saasy](./LICENSE.md)
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bffless/skills",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "BFFless platform skills — usable with Claude Code or any agent via the `skills` CLI",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"skills",
|
|
7
|
+
"agent-skills",
|
|
8
|
+
"claude-code-plugin",
|
|
9
|
+
"bffless",
|
|
10
|
+
"ai",
|
|
11
|
+
"static-hosting"
|
|
12
|
+
],
|
|
13
|
+
"author": "BFFless",
|
|
14
|
+
"license": "O'Saasy",
|
|
15
|
+
"homepage": "https://docs.bffless.app",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/bffless/skills"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
".claude-plugin/marketplace.json",
|
|
22
|
+
"plugins/"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bffless",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "BFFless platform skills for Claude Code — deployments, pipelines, proxy rules, chat, traffic splitting, and more",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "BFFless"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://docs.bffless.app",
|
|
9
|
+
"repository": "https://github.com/bffless/skills",
|
|
10
|
+
"license": "O'Saasy",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"bffless",
|
|
13
|
+
"static-hosting",
|
|
14
|
+
"deployments",
|
|
15
|
+
"pipelines",
|
|
16
|
+
"chat"
|
|
17
|
+
],
|
|
18
|
+
"skills": [
|
|
19
|
+
"./skills/"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authentication
|
|
3
|
+
description: Cross-domain authentication using the admin login relay pattern, built-in /_bffless/auth endpoints, and cookie-based sessions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authentication
|
|
7
|
+
|
|
8
|
+
BFFless uses a cross-domain authentication relay pattern. Users authenticate at the workspace's admin domain (`admin.<workspace>`) and are relayed back to the content domain with auth cookies. Auth endpoints on the content domain are accessed via the **built-in `/_bffless/auth/*` endpoints** — no proxy rules required.
|
|
9
|
+
|
|
10
|
+
## How Authentication Works
|
|
11
|
+
|
|
12
|
+
### Workspace Subdomains
|
|
13
|
+
|
|
14
|
+
For workspace subdomains (e.g., `myalias.sandbox.workspace.bffless.app`), SuperTokens session cookies (`sAccessToken`) work directly because they share the parent domain.
|
|
15
|
+
|
|
16
|
+
When a user visits a private deployment and isn't authenticated:
|
|
17
|
+
|
|
18
|
+
1. Backend redirects to `https://admin.<workspace>/login?redirect=<original-path>&tryRefresh=true`
|
|
19
|
+
2. The login page attempts a session refresh first (the `tryRefresh` param)
|
|
20
|
+
3. If refresh fails, the user logs in normally
|
|
21
|
+
4. After login, the user is redirected back to the original path
|
|
22
|
+
5. The `sAccessToken` cookie is valid across all subdomains of the workspace
|
|
23
|
+
|
|
24
|
+
### Custom Domains (customDomainRelay)
|
|
25
|
+
|
|
26
|
+
For custom domains (e.g., `www.bffless.com`), SuperTokens cookies don't work because they're on a completely different domain. BFFless uses a **domain relay** flow:
|
|
27
|
+
|
|
28
|
+
1. User visits a private page on `www.bffless.com/portal/`
|
|
29
|
+
2. Frontend detects the user is not authenticated (via `/_bffless/auth/session`)
|
|
30
|
+
3. Frontend redirects to the admin login with relay params:
|
|
31
|
+
```
|
|
32
|
+
https://admin.console.bffless.app/login?customDomainRelay=true&targetDomain=www.bffless.com&redirect=%2Fportal%2F
|
|
33
|
+
```
|
|
34
|
+
4. User logs in on the admin domain (or is already logged in via SuperTokens session)
|
|
35
|
+
5. After login, the frontend calls `POST /api/auth/domain-token` with:
|
|
36
|
+
```json
|
|
37
|
+
{ "targetDomain": "www.bffless.com", "redirectPath": "/portal/" }
|
|
38
|
+
```
|
|
39
|
+
6. Backend validates that `targetDomain` is a registered domain for this workspace, then creates a short-lived JWT (the "domain token")
|
|
40
|
+
7. Backend returns a `redirectUrl` pointing to the callback on the custom domain: `https://www.bffless.com/_bffless/auth/callback?token=...&redirect=/portal/`
|
|
41
|
+
8. The callback endpoint validates the token, sets `bffless_access` and `bffless_refresh` HttpOnly cookies, and redirects to the original path
|
|
42
|
+
|
|
43
|
+
### Important: Use `/_bffless/auth/*`, NOT `/api/auth/*`
|
|
44
|
+
|
|
45
|
+
The `/_bffless/auth/*` endpoints are **built into BFFless nginx** and handled by a dedicated controller. They are separate from the SuperTokens `/api/auth/*` endpoints. Do NOT use `/api/auth/*` on custom domains — those are SuperTokens endpoints that use different cookies (`sAccessToken`) which are not set by the domain relay flow.
|
|
46
|
+
|
|
47
|
+
The domain relay callback sets `bffless_access` and `bffless_refresh` cookies, which are only recognized by the `/_bffless/auth/*` endpoints. Using `/api/auth/session` instead of `/_bffless/auth/session` will cause a redirect loop because the SuperTokens session check won't find the `bffless_access` cookie.
|
|
48
|
+
|
|
49
|
+
## Auth Endpoints (Built-in)
|
|
50
|
+
|
|
51
|
+
All auth endpoints are available at `/_bffless/auth/*` on any domain served by BFFless — no proxy rules needed.
|
|
52
|
+
|
|
53
|
+
| Endpoint | Method | Purpose |
|
|
54
|
+
| --------------------------- | ------ | -------------------------------------------------------- |
|
|
55
|
+
| `/_bffless/auth/session` | GET | Check current session (returns user info or 401) |
|
|
56
|
+
| `/_bffless/auth/refresh` | POST | Refresh an expired access token using the refresh cookie |
|
|
57
|
+
| `/_bffless/auth/callback` | GET | Exchange a domain relay token for auth cookies |
|
|
58
|
+
| `/_bffless/auth/logout` | POST | Clear auth cookies |
|
|
59
|
+
|
|
60
|
+
### Session Check Priority
|
|
61
|
+
|
|
62
|
+
The `/_bffless/auth/session` endpoint checks auth in this order:
|
|
63
|
+
|
|
64
|
+
1. **`bffless_access` cookie** — custom domain JWT issued by the callback flow
|
|
65
|
+
2. **`sAccessToken` cookie** — SuperTokens session (fallback for workspace subdomains)
|
|
66
|
+
|
|
67
|
+
If the access token is expired, it returns `401` with `"try refresh token"` to signal the client should call `/_bffless/auth/refresh`.
|
|
68
|
+
|
|
69
|
+
## Frontend Integration
|
|
70
|
+
|
|
71
|
+
### Checking Session (with automatic token refresh)
|
|
72
|
+
|
|
73
|
+
Use a shared promise pattern to avoid duplicate session checks across components:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
async function checkSession() {
|
|
77
|
+
// Reuse shared session promise so multiple components don't duplicate requests
|
|
78
|
+
if (!(window as any).__bfflessSession) {
|
|
79
|
+
(window as any).__bfflessSession = (async () => {
|
|
80
|
+
const res = await fetch('/_bffless/auth/session', { credentials: 'include' });
|
|
81
|
+
if (res.ok) return res.json();
|
|
82
|
+
|
|
83
|
+
if (res.status === 401) {
|
|
84
|
+
// Token expired — try refreshing
|
|
85
|
+
const refreshRes = await fetch('/_bffless/auth/refresh', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
credentials: 'include',
|
|
88
|
+
});
|
|
89
|
+
if (refreshRes.ok) {
|
|
90
|
+
// Retry session check with new token
|
|
91
|
+
const retryRes = await fetch('/_bffless/auth/session', { credentials: 'include' });
|
|
92
|
+
if (retryRes.ok) return retryRes.json();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
})().catch(() => null);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (window as any).__bfflessSession;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Returns: { authenticated: true, user: { id, email, role } } or null
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The flow is: session check → if 401, refresh token → retry session check. This handles the common case where the access token has expired but the refresh token is still valid.
|
|
106
|
+
|
|
107
|
+
### Redirecting to Login
|
|
108
|
+
|
|
109
|
+
When unauthenticated, redirect the user to the admin login with relay params. Use the **promoted admin domain** (e.g., `admin.console.bffless.app`), not the full workspace subdomain:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
function getLoginUrl(adminLoginUrl: string, redirectPath: string): string {
|
|
113
|
+
const targetDomain = window.location.hostname;
|
|
114
|
+
const params = new URLSearchParams({
|
|
115
|
+
customDomainRelay: 'true',
|
|
116
|
+
targetDomain,
|
|
117
|
+
redirect: redirectPath,
|
|
118
|
+
});
|
|
119
|
+
return `${adminLoginUrl}?${params.toString()}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Example: redirect to admin login, then relay back to /portal/
|
|
123
|
+
const session = await checkSession();
|
|
124
|
+
if (!session) {
|
|
125
|
+
window.location.href = getLoginUrl(
|
|
126
|
+
'https://admin.console.bffless.app/login',
|
|
127
|
+
'/portal/',
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Logout
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
await fetch('/_bffless/auth/logout', { method: 'POST', credentials: 'include' });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Updating UI Based on Auth State (Header example)
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Check auth state and update Login/Portal links
|
|
142
|
+
window.__bfflessSession = window.__bfflessSession || checkBfflessSession().catch(() => null);
|
|
143
|
+
|
|
144
|
+
window.__bfflessSession.then((data) => {
|
|
145
|
+
if (data?.authenticated) {
|
|
146
|
+
// User is logged in — update nav links
|
|
147
|
+
document.querySelectorAll('[data-auth-link]').forEach((el) => {
|
|
148
|
+
el.textContent = 'Portal';
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Auth Flow Diagram
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Custom Domain Flow:
|
|
158
|
+
┌──────────────────┐ JS redirect ┌──────────────────────────┐
|
|
159
|
+
│ www.bffless.com │ ──────────────────→ │ admin.<workspace>/login │
|
|
160
|
+
│ (private page) │ customDomainRelay= │ ?customDomainRelay=true │
|
|
161
|
+
│ │ true&targetDomain= │ &targetDomain=www... │
|
|
162
|
+
└──────────────────┘ www.bffless.com └────────────┬─────────────┘
|
|
163
|
+
▲ │
|
|
164
|
+
│ User logs in (SuperTokens)
|
|
165
|
+
│ │
|
|
166
|
+
│ ▼
|
|
167
|
+
│ POST /api/auth/domain-token
|
|
168
|
+
│ → returns { token, redirectUrl }
|
|
169
|
+
│ │
|
|
170
|
+
│ 302 redirect │
|
|
171
|
+
│ ←─────────────────────────────────────────────┘
|
|
172
|
+
│ to: www.bffless.com/_bffless/auth/callback?token=...
|
|
173
|
+
│
|
|
174
|
+
▼
|
|
175
|
+
┌──────────────────┐
|
|
176
|
+
│ /_bffless/auth │ Validates token, sets bffless_access
|
|
177
|
+
│ /callback │ + bffless_refresh cookies
|
|
178
|
+
│ (built-in) │ → 302 redirect to /portal/
|
|
179
|
+
└──────────────────┘
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Troubleshooting
|
|
183
|
+
|
|
184
|
+
**User gets stuck in a redirect loop?**
|
|
185
|
+
|
|
186
|
+
- **Most common cause:** Using `/api/auth/session` instead of `/_bffless/auth/session`. The domain relay callback sets `bffless_access` cookies which are only recognized by `/_bffless/auth/*` endpoints. The `/api/auth/*` endpoints check SuperTokens cookies (`sAccessToken`) which are NOT set by the domain relay flow.
|
|
187
|
+
- Verify the custom domain is registered in `domain_mappings` with `isActive = true`
|
|
188
|
+
- Ensure cookies are being set (requires HTTPS for `Secure` flag)
|
|
189
|
+
|
|
190
|
+
**"Domain not registered" error on domain-token?**
|
|
191
|
+
|
|
192
|
+
- The `targetDomain` must match a `domain_mappings` entry or be a subdomain of `PRIMARY_DOMAIN`
|
|
193
|
+
- Check for www vs non-www mismatch
|
|
194
|
+
|
|
195
|
+
**Session check returns 401 but user just logged in?**
|
|
196
|
+
|
|
197
|
+
- On custom domains: verify the `/_bffless/auth/callback` was reached and cookies were set
|
|
198
|
+
- On workspace subdomains: verify `COOKIE_DOMAIN` is configured for cross-subdomain cookie sharing
|
|
199
|
+
- Check that the `bffless_access` or `sAccessToken` cookie is present in the request
|
|
200
|
+
|
|
201
|
+
**Admin login URL — use promoted domain, not workspace subdomain:**
|
|
202
|
+
|
|
203
|
+
- If the workspace has a promoted domain (e.g., `console.bffless.app`), use `admin.console.bffless.app`, NOT `admin.console.workspace.bffless.app`
|
|
204
|
+
- The workspace subdomain format still works but the promoted domain is cleaner
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authorization
|
|
3
|
+
description: Two-level permission system with global and project roles
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authorization
|
|
7
|
+
|
|
8
|
+
**Docs**: https://docs.bffless.app/features/authorization/
|
|
9
|
+
|
|
10
|
+
BFFless uses a two-level permission system: global roles for workspace-wide access and project roles for fine-grained control.
|
|
11
|
+
|
|
12
|
+
## Global Roles
|
|
13
|
+
|
|
14
|
+
| Role | Capabilities |
|
|
15
|
+
|------|-------------|
|
|
16
|
+
| **Admin** | Full workspace access, manage users, billing, settings |
|
|
17
|
+
| **User** | Create projects, manage own content |
|
|
18
|
+
| **Member** | View-only access, participate in assigned projects |
|
|
19
|
+
|
|
20
|
+
## Project Roles
|
|
21
|
+
|
|
22
|
+
| Role | Capabilities |
|
|
23
|
+
|------|-------------|
|
|
24
|
+
| **Owner** | Full control, delete project, transfer ownership |
|
|
25
|
+
| **Admin** | Manage settings, users, deployments |
|
|
26
|
+
| **Contributor** | Deploy, create aliases, modify content |
|
|
27
|
+
| **Viewer** | Read-only access to project and deployments |
|
|
28
|
+
|
|
29
|
+
## Permission Sources
|
|
30
|
+
|
|
31
|
+
Users can receive project access from multiple sources:
|
|
32
|
+
|
|
33
|
+
1. **Direct assignment**: Explicitly added to project
|
|
34
|
+
2. **Group membership**: Inherited from user group
|
|
35
|
+
3. **Effective role**: Highest permission wins when multiple sources exist
|
|
36
|
+
|
|
37
|
+
## API Keys
|
|
38
|
+
|
|
39
|
+
Two types of API keys for automation:
|
|
40
|
+
|
|
41
|
+
- **Global keys**: Workspace-wide access, use the creator's permissions
|
|
42
|
+
- **Project keys**: Scoped to single project, specify exact role
|
|
43
|
+
|
|
44
|
+
Best practice: Use project-scoped keys with minimum required permissions.
|
|
45
|
+
|
|
46
|
+
## Common Patterns
|
|
47
|
+
|
|
48
|
+
**External contractors**: Add as Member globally, Contributor on specific projects
|
|
49
|
+
|
|
50
|
+
**CI/CD pipelines**: Create project API key with Contributor role
|
|
51
|
+
|
|
52
|
+
**Client preview access**: Use share links instead of user accounts
|
|
53
|
+
|
|
54
|
+
## Troubleshooting
|
|
55
|
+
|
|
56
|
+
**"Permission denied" errors?**
|
|
57
|
+
- Check effective role (may be inherited from group)
|
|
58
|
+
- Verify API key scope matches the project
|
|
59
|
+
- Global Members need explicit project assignment
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bffless
|
|
3
|
+
description: Knowledge about BFFless platform, features, and setup
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# BFFless Platform Guide
|
|
7
|
+
|
|
8
|
+
BFFless is a self-hosted static asset hosting platform designed for modern frontend deployments. It provides a Backend-for-Frontend (BFF) layer without requiring you to write backend code.
|
|
9
|
+
|
|
10
|
+
## What is BFFless?
|
|
11
|
+
|
|
12
|
+
BFFless is a platform that:
|
|
13
|
+
|
|
14
|
+
- **Hosts static assets** from your CI/CD pipeline (React, Vue, Angular, etc.)
|
|
15
|
+
- **Provides AI-powered pipelines** for backend automation without writing code
|
|
16
|
+
- **Manages deployments** with aliases like `production`, `staging`, `preview`
|
|
17
|
+
- **Handles custom domains** with automatic SSL via Let's Encrypt
|
|
18
|
+
- **Enables traffic splitting** for A/B testing and canary deployments
|
|
19
|
+
- **Offers proxy rules** to forward requests to backend APIs
|
|
20
|
+
|
|
21
|
+
### Key Concepts
|
|
22
|
+
|
|
23
|
+
| Concept | Description |
|
|
24
|
+
| -------------- | --------------------------------------------------------------- |
|
|
25
|
+
| **Deployment** | A snapshot of your build artifacts at a specific commit SHA |
|
|
26
|
+
| **Alias** | A named pointer to a deployment (e.g., `production` → `abc123`) |
|
|
27
|
+
| **Proxy Rule** | Routes requests to external APIs or AI pipelines |
|
|
28
|
+
| **Pipeline** | Chain of handlers that process requests (AI, transform, proxy) |
|
|
29
|
+
| **Skill** | Markdown files that give AI agents specialized knowledge |
|
|
30
|
+
|
|
31
|
+
## Setting Up Proxy Rules
|
|
32
|
+
|
|
33
|
+
Proxy rules let you route requests through BFFless to backend APIs or AI pipelines.
|
|
34
|
+
|
|
35
|
+
### Creating a Proxy Rule
|
|
36
|
+
|
|
37
|
+
1. Go to **Project Settings** → **Proxy Rules**
|
|
38
|
+
2. Click **Add Rule**
|
|
39
|
+
3. Configure the rule:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Path Pattern: /api/*
|
|
43
|
+
Target URL: https://your-backend.com
|
|
44
|
+
Strip Prefix: true (removes /api from forwarded requests)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Rule Types
|
|
48
|
+
|
|
49
|
+
| Type | Use Case |
|
|
50
|
+
| -------------------- | ---------------------------------------- |
|
|
51
|
+
| **External Proxy** | Forward to external APIs (REST, GraphQL) |
|
|
52
|
+
| **Pipeline** | Process with AI handlers |
|
|
53
|
+
| **Internal Rewrite** | Serve different static files |
|
|
54
|
+
|
|
55
|
+
### Common Patterns
|
|
56
|
+
|
|
57
|
+
**API Gateway:**
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
/api/* → https://api.example.com/*
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**AI Chat Endpoint:**
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
/api/chat → Pipeline with AI handler
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Microservices:**
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
/api/users/* → https://users-service.internal
|
|
73
|
+
/api/orders/* → https://orders-service.internal
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Traffic Splitting
|
|
77
|
+
|
|
78
|
+
Traffic splitting distributes requests across multiple deployment aliases for A/B testing or gradual rollouts.
|
|
79
|
+
|
|
80
|
+
### How It Works
|
|
81
|
+
|
|
82
|
+
1. Configure weights per alias (e.g., 90% production, 10% canary)
|
|
83
|
+
2. First-time visitors get randomly assigned based on weights
|
|
84
|
+
3. A cookie (`__bffless_variant`) maintains sticky sessions
|
|
85
|
+
4. Users see consistent experience on return visits
|
|
86
|
+
|
|
87
|
+
### Setting Up Traffic Splitting
|
|
88
|
+
|
|
89
|
+
1. Go to **Domains** → Select your domain
|
|
90
|
+
2. Click **Traffic Splitting**
|
|
91
|
+
3. Add aliases with weights:
|
|
92
|
+
|
|
93
|
+
| Alias | Weight |
|
|
94
|
+
| ---------- | ------ |
|
|
95
|
+
| production | 90% |
|
|
96
|
+
| canary | 10% |
|
|
97
|
+
|
|
98
|
+
4. Save changes
|
|
99
|
+
|
|
100
|
+
### Use Cases
|
|
101
|
+
|
|
102
|
+
- **A/B Testing**: Compare two versions of your app
|
|
103
|
+
- **Canary Deployments**: Gradually roll out new features
|
|
104
|
+
- **Blue/Green**: Instant switch between deployments
|
|
105
|
+
- **Feature Flags**: Route specific traffic to experimental builds
|
|
106
|
+
|
|
107
|
+
### Best Practices
|
|
108
|
+
|
|
109
|
+
- Start with small percentages for new deployments (5-10%)
|
|
110
|
+
- Monitor error rates before increasing traffic
|
|
111
|
+
- Use the `X-Variant` header to track which variant served the request
|
|
112
|
+
- Cookie duration is configurable (default: 24 hours)
|
|
113
|
+
|
|
114
|
+
## Pipelines and AI Handlers
|
|
115
|
+
|
|
116
|
+
Pipelines let you create backend logic without writing code.
|
|
117
|
+
|
|
118
|
+
### Pipeline Structure
|
|
119
|
+
|
|
120
|
+
A pipeline is a chain of steps:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Request → Step 1 → Step 2 → Step 3 → Response
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### AI Handler
|
|
127
|
+
|
|
128
|
+
The AI handler uses LLMs to process requests:
|
|
129
|
+
|
|
130
|
+
- Configure system prompts
|
|
131
|
+
- Set model and temperature
|
|
132
|
+
- Enable skills for specialized knowledge
|
|
133
|
+
- Stream responses for chat interfaces
|
|
134
|
+
|
|
135
|
+
### Example: Chat API
|
|
136
|
+
|
|
137
|
+
1. Create a pipeline with an AI handler
|
|
138
|
+
2. Configure:
|
|
139
|
+
- Model: `gpt-4` or `claude-3`
|
|
140
|
+
- System Prompt: Your assistant instructions
|
|
141
|
+
- Skills: Enable relevant skills
|
|
142
|
+
3. Create a proxy rule pointing to the pipeline
|
|
143
|
+
4. Your frontend calls `/api/chat` and gets AI responses
|
|
144
|
+
|
|
145
|
+
## GitHub Action Integration
|
|
146
|
+
|
|
147
|
+
Upload builds automatically from CI/CD:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
- uses: bffless/upload-artifact@v1
|
|
151
|
+
with:
|
|
152
|
+
path: dist
|
|
153
|
+
api-url: ${{ vars.BFFLESS_URL }}
|
|
154
|
+
api-key: ${{ secrets.BFFLESS_KEY }}
|
|
155
|
+
alias: production
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This uploads your build and updates the `production` alias to point to it.
|