Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
0780b51
add env variable to disable multi-tenancy
harisadam Dec 9, 2025
fec93e2
fix typo
harisadam Dec 10, 2025
bec6036
refactor
harisadam Dec 10, 2025
ab5a5c4
refactor: tenant config might be better in a separate concern
harisadam Dec 10, 2025
64ca9e5
fix linting
harisadam Dec 10, 2025
eb8655d
refactor: use MULTI_TENANT env variable, model concern instead of con…
harisadam Dec 10, 2025
331ae3c
Fixing Lexxy prompt menu spacing
zoltanhosszu Dec 8, 2025
78a4d27
Rails seeded parallel tests (#2037)
jeremy Dec 9, 2025
bbe6ac0
Bump Rails to current ast-immediate-variants-process-locally branch
jeremy Dec 9, 2025
cc64c14
Add blank slate to the main menu
andyra Dec 9, 2025
0d137f6
Allow chromium unstable endpoint
robinbrandt Dec 10, 2025
d840a0b
Remove semver-major-days from Dependabot on GH actions
monorkin Dec 10, 2025
b567848
allow configuring Active Storage service and add AWS S3 definition
hosmelq Dec 7, 2025
4cd9d5d
Update config/environments/production.rb
hosmelq Dec 5, 2025
30edeb6
rename Active Storage service to s3 and add checksum env
hosmelq Dec 5, 2025
db095cd
Don't overwrite storage service from engine
kevinmcconnell Dec 10, 2025
110b9f5
Add API to boards
dhh Dec 1, 2025
6820630
Add access token authentication via HTTP AUTHORIZATION bearer header
dhh Dec 1, 2025
e3c72c7
Test the boards API
dhh Dec 1, 2025
1ade22a
API index for cards
dhh Dec 1, 2025
330b15b
Correct
dhh Dec 1, 2025
8f626fd
Tie access token directly to session
dhh Dec 1, 2025
cfb494a
Add developer section to user profile
jzimdars Dec 2, 2025
45d2d8d
List, create, and revoke access tokens
jzimdars Dec 2, 2025
3fded9e
Authenticate api requests without needing a session
dhh Dec 2, 2025
a18d349
Drop the need for access tokens to have a session
dhh Dec 2, 2025
b91daaa
Handle everything in the same method
dhh Dec 2, 2025
b56ec33
Inline now anemic helper methods
dhh Dec 2, 2025
22a50a8
Clarify
dhh Dec 2, 2025
9ef3fd2
The magic of it is not needing to manually yield it!
dhh Dec 2, 2025
c52ed02
This had gotten stripped
dhh Dec 2, 2025
eb04f8c
Smooth out the finder API
dhh Dec 2, 2025
f6e637e
Access tokens are strictly personal
dhh Dec 2, 2025
ed0f7ca
Inline anemic partial
dhh Dec 2, 2025
3a54604
Only allow new token to be viewed within 10 seconds
dhh Dec 2, 2025
54300fd
Polish
dhh Dec 2, 2025
2b9d0ac
Awaiting JZ's design
dhh Dec 2, 2025
8f0df2c
Only allow writing when the access token has permission
dhh Dec 2, 2025
28f0f93
Allow API JSON requests to sidestep csrf protection
dhh Dec 2, 2025
8c8f3e9
Creating a new board will return the location header
dhh Dec 2, 2025
c26efb9
Return json URLs for API actions
dhh Dec 2, 2025
6a620f6
Create cards via API
dhh Dec 2, 2025
0f7ee6f
Design show view
jzimdars Dec 2, 2025
937f72d
Complete the view transition loop
jzimdars Dec 2, 2025
4cd275b
Use built-in authenticate_or_request_with_http_token
dhh Dec 3, 2025
4a6a455
Add API support for users
jayohms Dec 3, 2025
bcba9ed
Add top-level API index support for tags
jayohms Dec 3, 2025
a817dc3
Excess whitespace
dhh Dec 3, 2025
8264e5e
Only authenticate with bearer token if the header is present
dhh Dec 3, 2025
b7f9786
Compact
dhh Dec 3, 2025
3d70ced
Publish any API card as soon as it is created
dhh Dec 3, 2025
71d05f8
Include card description and tags
dhh Dec 3, 2025
c23cd48
Fix quoting
dhh Dec 3, 2025
d8a5c74
Add an /identity.json endpoint to obtain the identity accounts and users
jayohms Dec 4, 2025
dfc34fb
Fix identity tests
monorkin Dec 4, 2025
2da6d8e
Fix Current not setting a session in some contexts
monorkin Dec 4, 2025
10f353d
Move tests into their controller tests
monorkin Dec 5, 2025
e9e09e6
Add card update & delete actions
monorkin Dec 5, 2025
731b8e2
Add API for assigning cards
monorkin Dec 5, 2025
139bb51
Add API for mobing cards between boards
monorkin Dec 5, 2025
ecdaf1d
Add API for closing and opening cards
monorkin Dec 5, 2025
f09c87c
Add API for comments CRUD
monorkin Dec 5, 2025
ac59992
Add API for gilding cards
monorkin Dec 5, 2025
c77e88a
Add API for removing card images
monorkin Dec 5, 2025
a50425c
Add API for postponing cards
monorkin Dec 5, 2025
a5fcafc
Add API for CRUD actions on steps
monorkin Dec 5, 2025
0967bb1
Add API for tagging cards
monorkin Dec 5, 2025
082b8f2
Add API for card triage
monorkin Dec 5, 2025
cadea2b
Add API for watching cards
monorkin Dec 5, 2025
a1dfb12
Add API for reactions
monorkin Dec 5, 2025
75a2787
Add API for columns
monorkin Dec 9, 2025
d21e1ff
Add API for updating and deactivating users
monorkin Dec 9, 2025
93bba1c
Replace external_account_id with slug
monorkin Dec 9, 2025
4111ca2
Add pagination to most places and fix cards pagination
monorkin Dec 9, 2025
4e521e0
Add API for creating and updating boards
monorkin Dec 9, 2025
a8ed47c
Add API for reading notifications
monorkin Dec 9, 2025
9b1c3ef
Lower the number of returned unread notifications
monorkin Dec 9, 2025
50718b6
Ignore documentation in Docker images
monorkin Dec 9, 2025
3259699
Document notifications endpoints
monorkin Dec 9, 2025
97538fd
Document users API endpoints
monorkin Dec 9, 2025
94a988b
Add detailed guide for creating access tokens
monorkin Dec 9, 2025
1b02a91
Add steps to cards
monorkin Dec 9, 2025
5b1ef76
Document Card-related APIs
monorkin Dec 9, 2025
de21a99
Add index actions for Comments and Columns
monorkin Dec 9, 2025
3e7ee4a
Move the tags section close to the cards
monorkin Dec 9, 2025
53ace01
Remove endpoints section
monorkin Dec 9, 2025
8bc7c65
Add cache directives
monorkin Dec 9, 2025
b779e25
Remove composite cache key
monorkin Dec 9, 2025
ee40e0c
Handle user update failures
monorkin Dec 10, 2025
bcb5560
Remove redundant respond_to
monorkin Dec 10, 2025
b56fa57
Return no content on update
monorkin Dec 10, 2025
d83e3c8
Move Identities to My::Identities
monorkin Dec 10, 2025
2b4f4b5
Inline partials
monorkin Dec 10, 2025
2ffeef4
Rename /identity to /my/identity in docs
monorkin Dec 10, 2025
fea46a1
Add validation for the join code usage limit
flavorjones Dec 10, 2025
4905293
Escape the names used to generate system comments
flavorjones Dec 9, 2025
fc57b81
Avoid unescaping characters when auto-linking
flavorjones Dec 9, 2025
5ce96f5
Autolinking is more robust
flavorjones Dec 9, 2025
ae34e9c
In non-SaaS, run jobs in container by default
kevinmcconnell Dec 10, 2025
19111d9
Fix status and filter mistakes
monorkin Dec 10, 2025
da2d101
Fix crash due to missing partial
monorkin Dec 10, 2025
94b37b5
Improve phrasing
monorkin Dec 10, 2025
216a54a
Fix Lexxy prompt list padding by lowering rich-text specificity
andyra Dec 10, 2025
3edcd19
Revert "Fix Lexxy prompt list padding by lowering rich-text specificity"
andyra Dec 10, 2025
c62795b
Make labels for webhook switches clickable
nqst Dec 8, 2025
5343a8c
Make board publication switch icons clickable
nqst Dec 8, 2025
e9a3700
(Optional) Simpler hint
nqst Dec 8, 2025
2c2a665
Fix label text wrapping on narrow screens
nqst Dec 8, 2025
205d520
Cleanup
nqst Dec 8, 2025
3aaa2ae
Cleanup
nqst Dec 8, 2025
debcc18
Don't use IDs, wrap input inside label instead
nqst Dec 8, 2025
1c3bfa4
Cleanup: apply classes to `<form>`, remove extra div
nqst Dec 8, 2025
fd54924
Add `cursor-pointer` utility and apply to icon labels
nqst Dec 8, 2025
cc85c16
Try using `cursor-pointer` on text labels
nqst Dec 8, 2025
0be979c
make MySQL SSL mode configurable via env var (#2036)
hosmelq Dec 10, 2025
87b1ef9
Bump sigstore/cosign-installer from 3.9.2 to 4.0.0 (#2044)
dependabot[bot] Dec 10, 2025
b4dfcf2
Bump docker/metadata-action from 5.8.0 to 5.10.0 (#2045)
dependabot[bot] Dec 10, 2025
3549e2d
Bump docker/login-action from 3.5.0 to 3.6.0 (#2046)
dependabot[bot] Dec 10, 2025
64370de
Bump actions/checkout from 4 to 6 (#2047)
dependabot[bot] Dec 10, 2025
2610e05
Get gitleaks-audit green again
flavorjones Dec 10, 2025
c917b23
Gitleaks: ignore legit non-sensitive API keys and tokens in docs/ and…
jeremy Dec 10, 2025
c34329e
merge conflict
jacobhrussell Dec 11, 2025
c92e671
CSP: full config with env vars per source (#2069)
jeremy Dec 11, 2025
d0a28ed
Enforce CSP (#2070)
jeremy Dec 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
# Ignore bundler config.
/.bundle

# Ignore documentation
/docs/
/README.md
/CLAUDE.md
/AGENTS.md
/STYLE.md
/CONTRIBUTING.md

# Ignore all environment files (except templates).
/.env*
!/.env*.erb
Expand Down
1 change: 0 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,3 @@ updates:
open-pull-requests-limit: 10
cooldown:
default-days: 7
semver-major-days: 14
4 changes: 2 additions & 2 deletions .github/workflows/ci-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
Expand All @@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ jobs:
IMAGE_NAME: ${{ github.repository }}
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/[email protected]

- name: Log in to GHCR
uses: docker/login-action@v3.5.0
uses: docker/login-action@v3.6.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
Expand All @@ -62,7 +62,7 @@ jobs:

- name: Extract Docker metadata (tags, labels) with arch suffix
id: meta
uses: docker/metadata-action@v5.8.0
uses: docker/metadata-action@v5.10.0
with:
images: ${{ steps.vars.outputs.canonical }}
tags: |
Expand Down Expand Up @@ -118,7 +118,7 @@ jobs:
uses: docker/[email protected]

- name: Log in to GHCR
uses: docker/login-action@v3.5.0
uses: docker/login-action@v3.6.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
Expand All @@ -135,7 +135,7 @@ jobs:

- name: Compute base tags (no suffix)
id: meta
uses: docker/metadata-action@v5.8.0
uses: docker/metadata-action@v5.10.0
with:
images: ${{ steps.vars.outputs.canonical }}
tags: |
Expand Down Expand Up @@ -179,7 +179,7 @@ jobs:
done <<< "$tags"

- name: Install Cosign
uses: sigstore/cosign-installer@v3.9.2
uses: sigstore/cosign-installer@v4.0.0

- name: Cosign sign all tags (keyless OIDC)
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Install system packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg

- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: ruby/setup-ruby@v1
with:
Expand Down
2 changes: 2 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ paths = [
'''log''',
'''tmp''',
'''.*\.yml\.enc''',
'''docs/''',
'''test/''',
]
6 changes: 0 additions & 6 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
d8463077:gems/fizzy-saas/bin/setup:generic-api-key:54
c4073c1c:app/models/integration/basecamp.rb:generic-api-key:3
c4073c1c:app/models/integration/basecamp.rb:generic-api-key:4
02a42167:test/models/webhook_test.rb:slack-webhook-url:57
2fc9215b:test/models/webhook/delivery_test.rb:slack-webhook-url:156
2fc9215b:test/models/webhook_test.rb:slack-webhook-url:57
a515ea3b:test/fixtures/webhooks.yml:generic-api-key:5
a515ea3b:test/fixtures/webhooks.yml:generic-api-key:11
1f21c12c:test/vcr_cassettes/command/ai/translator_test-test_combine_commands_and_filters.yml:github-oauth:73012
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ GIT

GIT
remote: https://github.com/rails/rails.git
revision: bf81d40a91880c059ce9d0447219385a2c8e4a15
revision: 0636a79d1bf268db6cdbbc6327106d08c3ff3751
branch: ast-immediate-variants-process-locally
specs:
actioncable (8.2.0.alpha)
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.saas.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ GIT

GIT
remote: https://github.com/basecamp/fizzy-saas
revision: 43dbc896ce7b6a08194a92ddd1695d3f1ebf554b
revision: a14df11b57818697df4b2cc7b6a43e762ebaa196
specs:
fizzy-saas (0.1.0)
audits1984
Expand Down Expand Up @@ -82,7 +82,7 @@ GIT

GIT
remote: https://github.com/rails/rails.git
revision: bf81d40a91880c059ce9d0447219385a2c8e4a15
revision: 0636a79d1bf268db6cdbbc6327106d08c3ff3751
branch: ast-immediate-variants-process-locally
specs:
actioncable (8.2.0.alpha)
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ We've added comments to that file to highlight what each setting needs to be, bu
- `proxy/ssl` and `proxy/host`: Kamal can set up SSL certificates for you automatically. To enable that, set the hostname again as `host`. If you don't want SSL for some reason, you can set `ssl: false` to turn it off.
- `env/clear/MAILER_FROM_ADDRESS`: This is the email address that Fizzy will send emails from. It should usually be an address from the same domain where you're running Fizzy.
- `env/clear/SMTP_ADDRESS`: The address of an SMTP server that you can send email through. You can use a 3rd-party service for this, like Sendgrid or Postmark, in which case their documentation will tell you what to use for this.
- `env/clear/MULTI_TENANT`: Set to `false` to enable single-tenant mode (disable multi-account signups).


Fizzy also requires a few environment variables to be set up, some of which contain secrets.
The simplest way to do this is to put them in a file called `.kamal/secrets`.
Expand Down Expand Up @@ -93,6 +95,25 @@ After the first deploy is done, any subsequent steps won't need to do that initi
bin/kamal deploy
```

## File storage (Active Storage)

Production uses the local disk service by default. To use any other service defined in `config/storage.yml`, set `ACTIVE_STORAGE_SERVICE`.

To use the included `s3` service, set:

- `ACTIVE_STORAGE_SERVICE=s3`
- `S3_ACCESS_KEY_ID`
- `S3_BUCKET` (defaults to `fizzy-#{Rails.env}-activestorage`)
- `S3_REGION` (defaults to `us-east-1`)
- `S3_SECRET_ACCESS_KEY`

Optional for S3-compatible endpoints:

- `S3_ENDPOINT`
- `S3_FORCE_PATH_STYLE=true`
- `S3_REQUEST_CHECKSUM_CALCULATION` (defaults to `when_supported`)
- `S3_RESPONSE_CHECKSUM_VALIDATION` (defaults to `when_supported`)

## Development

### Setting up
Expand Down
19 changes: 19 additions & 0 deletions app/assets/stylesheets/access-tokens.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.access_tokens_table {
border-collapse: collapse;
inline-size: 100%;

td, th {
border-block-end: 1px solid var(--color-ink-light);
padding-inline: var(--inline-space);
text-align: start;
}

th {
font-size: var(--text-x-small);
text-transform: uppercase;
}

tr:nth-of-type(even) {
background-color: var(--color-ink-lightest);
}
}
4 changes: 4 additions & 0 deletions app/assets/stylesheets/lexxy.css
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@

max-height: 200px;
overflow: scroll;

&:is(.lexxy-prompt-menu--visible) {
padding: var(--lexxy-prompt-padding);
}
}

.lexxy-prompt-menu--visible {
Expand Down
9 changes: 9 additions & 0 deletions app/assets/stylesheets/nav.css
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,15 @@
}
}

.nav__blank-slate {
font-size: var(--text-small);
margin: 1rem auto;

.nav:has(.popup__item:not([hidden])) & {
display: none;
}
}

.nav__footer {
background-color: var(--color-canvas);
border-block-start: 1px solid var(--color-ink-lighter);
Expand Down
2 changes: 2 additions & 0 deletions app/assets/stylesheets/utilities.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
pointer-events: none;
}

.cursor-pointer { cursor: pointer; }

/* Padding */
.pad { padding: var(--block-space) var(--inline-space); }
.pad-double { padding: var(--block-space-double) var(--inline-space-double); }
Expand Down
7 changes: 5 additions & 2 deletions app/controllers/account/join_codes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ def edit
end

def update
@join_code.update!(join_code_params)
redirect_to account_join_code_path
if @join_code.update(join_code_params)
redirect_to account_join_code_path
else
render :edit, status: :unprocessable_entity
end
end

def destroy
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/boards/columns_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,41 @@ class Boards::ColumnsController < ApplicationController

before_action :set_column, only: %i[ show update destroy ]

def index
@columns = @board.columns.sorted
fresh_when etag: @columns
end

def show
set_page_and_extract_portion_from @column.cards.active.latest.with_golden_first.preloaded
fresh_when etag: @page.records
end

def create
@column = @board.columns.create!(column_params)

respond_to do |format|
format.turbo_stream
format.json { head :created, location: board_column_path(@board, @column, format: :json) }
end
end

def update
@column.update!(column_params)

respond_to do |format|
format.turbo_stream
format.json { head :no_content }
end
end

def destroy
@column.destroy

respond_to do |format|
format.turbo_stream
format.json { head :no_content }
end
end

private
Expand Down
31 changes: 24 additions & 7 deletions app/controllers/boards_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
class BoardsController < ApplicationController
include FilterScoped

before_action :set_board, except: %i[ new create ]
before_action :set_board, except: %i[ index new create ]
before_action :ensure_permission_to_admin_board, only: %i[ update destroy ]

def index
set_page_and_extract_portion_from Current.user.boards
end

def show
if @filter.used?(ignore_boards: true)
show_filtered_cards
Expand All @@ -18,7 +22,11 @@ def new

def create
@board = Board.create! board_params.with_defaults(all_access: true)
redirect_to board_path(@board)

respond_to do |format|
format.html { redirect_to board_path(@board) }
format.json { head :created, location: board_path(@board, format: :json) }
end
end

def edit
Expand All @@ -31,16 +39,25 @@ def update
@board.update! board_params
@board.accesses.revise granted: grantees, revoked: revokees if grantees_changed?

if @board.accessible_to?(Current.user)
redirect_to edit_board_path(@board), notice: "Saved"
else
redirect_to root_path, notice: "Saved (you were removed from the board)"
respond_to do |format|
format.html do
if @board.accessible_to?(Current.user)
redirect_to edit_board_path(@board), notice: "Saved"
else
redirect_to root_path, notice: "Saved (you were removed from the board)"
end
end
format.json { head :no_content }
end
end

def destroy
@board.destroy
redirect_to root_path

respond_to do |format|
format.html { redirect_to root_path }
format.json { head :no_content }
end
end

private
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/cards/assignments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ def new

def create
@card.toggle_assignment @board.users.active.find(params[:assignee_id])

respond_to do |format|
format.turbo_stream
format.json { head :no_content }
end
end
end
6 changes: 5 additions & 1 deletion app/controllers/cards/boards_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ def edit

def update
@card.move_to(@board)
redirect_to @card

respond_to do |format|
format.html { redirect_to @card }
format.json { head :no_content }
end
end

private
Expand Down
12 changes: 10 additions & 2 deletions app/controllers/cards/closures_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ class Cards::ClosuresController < ApplicationController

def create
@card.close
render_card_replacement

respond_to do |format|
format.turbo_stream { render_card_replacement }
format.json { head :no_content }
end
end

def destroy
@card.reopen
render_card_replacement

respond_to do |format|
format.turbo_stream { render_card_replacement }
format.json { head :no_content }
end
end
end
Loading