本文へスキップ

機能

Seedに含まれるもの、まだないもの

Seedは無料でMITライセンスのSaaSスターターです。メールアドレス、Google、GitHubでのログイン、パスキー、マジックリンク、ゲストアカウント、2段階認証、ロール付きグループ、メンバー単位のStripe課金、リアルタイム更新、オフラインアプリ、管理画面、監視、そして任意で毎日の暗号化バックアップを備えています。以下の各セクションから、そのコードにリンクしています。

著者: Anton Shubin更新日:

Seedは今すぐ使えて、公開しながら開発中です。足りないものは、影響する機能の横に「まだないもの」と記し、issueがあればそのリンクも載せています。

面倒な部分は解決済み

グループと課金が最初から組み込まれたSaaSスターター

アカウント、グループ、お金。どのプロダクトでも最初の機能より前に必要で、しかも一番間違えやすい部分です。

現在使えるものCIでテスト済み。GoogleとGitHubでのログイン、Stripeには自分のキーが必要なため、デモではオフになっています。

アカウント

ログインは完成済み

ユーザーはメールアドレスとパスワードで登録し、コードでメールアドレスを確認します。忘れたパスワードの再設定、GoogleやGitHubでのログイン、パスキーでのログイン、そして登録も兼ねるメールのログインリンクにも対応。まずゲストアカウントでアプリを試して、あとからログイン方法を追加することもできます。認証アプリを使えば2段階認証(2FA)も有効にできます。ログイン中のデバイスはすべて一覧で確認でき、ほかのデバイスをログアウトさせることもできます。ログイン方法を変更するときは本人確認を求め、メールで通知します。

  • パスワードはWeb CryptoのPBKDF2-SHA-256でハッシュ化(デフォルトで60万回)
  • 署名付きのセッションCookieと、すべてのログインイベントの監査ログ
  • 個人用APIトークンは鍵付きハッシュとしてのみ保存
  • パスキー:保存するのは公開鍵だけで、ログインのチャレンジは1回限り有効
  • マジックリンク:1つのリンクでログインも登録もでき、1回限り・15分間有効で、ハッシュとしてのみ保存
  • ゲストアカウント(匿名ログイン):訪問者はログイン方法なしでワンクリックで始め、あとから同じアカウントにメールアドレスとパスワード、Google、GitHub、パスキーを追加できます
  • パスワードの誤入力は、ネットワークアドレスをまたいでログイン名ごとに数え、上限を超えると短時間ロック
プロフィールのセキュリティページ。パスワード、「パスキーを追加」ボタン、オンになった2段階認証、ログイン中のデバイス一覧の上部が見えます。

グループ

最初の1分から、ロール付きのグループ

すべてのアカウントに、登録時に個人用のグループが作られます。共有グループを作ってほかの人を招待し、閲覧者からオーナーまでのロールを割り当て、オーナー権限もワンステップで譲渡できます。削除したグループは30日以内なら復元でき、各グループにはアクティビティログが残ります。

  • テナントの境界はグループだけ。個人のデータも共有のデータも同じチェックを通ります
  • アプリでも、コードでも、このサイトでも、呼び名は「グループ」で統一
  • 権限モデルは1つだけ。2つ目と同期させ続ける手間はありません
  • 招待、ロール変更、オーナー権限の譲渡には、それぞれ専用のテスト
グループのアクティビティログ。誰がどのメモやタグをいつ追加したかが並び、各メモを開くリンクがあります。

概要

グループごとの概要画面

任意の期間の主要な数値とグラフ。ウィジェットの表示・非表示や並び順は人ごとに変えられ、そのレイアウトはほかのデバイスにも引き継がれます。

  • 各数値は前の期間と比較し、赤や緑で良し悪しを決めつけません
  • 表示するのは件数だけ。メモの本文や人のランキングは出しません
  • 読み込み、エラー、再試行はウィジェットごとに独立
  • 新しい集約は独自のウィジェットを追加でき、管理画面にも表示されます

お金

Stripeによるメンバー単位の課金

ユーザーには料金表、制限付きの無料プラン、有料プランの14日間無料トライアルが表示されます。オーナーは招待の前にメンバーを1人増やすといくらかかるかを確認でき、カード決済が失敗したときは期日入りのわかりやすい通知が届きます。

  • Stripe Checkoutとカスタマーポータル。プランの情報源はWebhookだけ
  • メンバー数に連動する数量
  • 支払い失敗後の猶予期間(デフォルトで7日間)
  • プランの上限はアプリだけでなくサーバーでもチェック
  • プランは1つのカタログファイルにまとまっていて、料金ページもそれを読み込みます
まだないもの
  • Stripe以外の決済サービス
サンプルグループの「プランと請求」タブ(デモ用の価格例)。更新日の入ったProプランで、月額€36.00、オーナーを含む4人のメンバー×各€9.00。カードの変更、請求書の表示、プランの解約のボタンがあります。

プライバシー

いつでも離れられるアカウント

ユーザーはプロフィールから自分のデータのコピーをダウンロードし、アカウントを削除できます。削除には7日間の猶予があります。既読の通知は90日後に削除されます。

  • データのダウンロード:JSONとCSVファイルのZIPをバックグラウンドで作成し、7日間保管
  • 削除は7日後に実行。もう一度ログインすると取り消されます
  • 既読の通知、無効になった招待、古い監査イベントを毎晩クリーンアップ

エクスポート

データは自分のもの

どのグループもオープンなJSONファイルにエクスポートでき、あとから新しいグループとしてインポートできます。元のグループが上書きされることはありません。

  • エクスポートはオーナーが行い、グループを作成できる人なら誰でもインポートできます
  • 書き込む前にファイル全体をチェックし、1ステップでインポート
  • メモとタグにはすべて新しいIDが振られるので、インポートしても既存のデータと衝突しません
  • 各集約がファイル内の自分の担当部分を登録します

言語

最初から9言語に対応

アプリ、管理画面、このWebサイトは英語、ロシア語、スペイン語、ドイツ語、ポルトガル語 (ブラジル)、アラビア語、フランス語、日本語、簡体中国語に対応しています。言語はアプリ内で選べて、アカウントに保存されます。このWebサイトの各ページは、言語ごとに専用のアドレスを持っています。

  • 言語ごとにTypeScriptのカタログが1つ。足りないメッセージや古くなったメッセージはテストがすべて指摘します
  • 複数形、数値、日付はプラットフォーム標準のIntlで処理。i18nライブラリは不要です
  • Webサイトの全ページにhreflangリンク、各言語を載せたサイトマップ
  • アラビア語のページは右から左に表示
  • メールは受信者の言語で届きます

必要になったときに、すぐ使える

リアルタイム、オフライン、バックグラウンド処理

難しいがゆえに多くのスターターが省いている部分です。ライブ更新、ネットワークなしで動くアプリ、そして失われてはならない処理。

現在使えるものオフラインでの書き込みはメモとタグに対応。そのほかのリストは、オフラインでも最後に表示した内容で開きます。

リアルタイム

再読み込みなしで変更が反映

メンバーの変更は、そのグループを開いているすべてのタブに表示されます。スリープから復帰したノートPCも、見逃した変更に追いつきます。

  • 標準のWebSocketで、すべての変更に連番付き
  • スリープから復帰したタブは再接続し、見逃した分を取得
  • リアルタイムはオフにもできます。その場合アプリは一定間隔でポーリングし、同じように動きます
1つ目のウィンドウ:メモエディターで、タイトルを「Q4ロードマップ:オンボーディング、課金、請求書」に変更したところ。
ウィンドウ1
2つ目のウィンドウ(別のログイン):保存した直後に、メモ一覧の先頭に新しいタイトルが再読み込みなしで表示されます。
ウィンドウ2

オフライン

ネットワークなしで開けるアプリ

アプリはネイティブアプリのようにインストールでき、ネットワークなしでも開けて、接続が戻るとためておいた変更を送信します。古くなった変更は解決すべき競合として表示し、黙って上書きすることはありません。

  • 各リストのIndexedDBコピーと、冪等キー付きの書き込みキュー
  • 競合ごとに「自分のバージョンを残す」か「サーバーのバージョンを使う」かを選択
  • 設定1つでオフにでき、取り除くこともできます。フォルダを1つ削除し、用意されたチェックリストに従うだけです
オフラインで行った変更が、サーバー上のより新しい変更とぶつかったところ。「1件の変更について選択が必要です」に、メモの2つのバージョンが並んで表示され、「自分のバージョンを残す」と「サーバーのバージョンを使う」のボタンがあります。

キーボード

アプリと管理画面のキーボードショートカット

?キーを押すと、すべてのショートカットが表示されます。Nキーでどのページからでもその場でメモを追加でき、Gキーに続けて文字キーを押すと各セクションが開きます。入力欄に入力している間は、ショートカットは邪魔をしません。

  • Ctrl K(Macでは⌘K)または/で検索を開く
  • アプリごとに1つのリストがキーを割り当て、?のダイアログにも表示するので、効かないキーが表示されることはありません
  • 1キーのショートカットはデバイスごとにオフにでき、その場合もCtrl KとEscは使えます
  • 管理画面では、Gキーに続けて文字キーを押すと各モジュールが開きます

通知

アプリ、スマホ、デスクトップ、Telegramへの通知

招待されたとき、新しいロールを割り当てられたとき、グループを譲渡されたときに通知が届きます。アプリ内、スマホやデスクトップへのWebプッシュ、そして連携していればTelegramにも。

  • 自分の鍵を使うWebプッシュ。鍵はタスク1つで作成:deno task vapid-key:create
  • アプリ内の未読件数と「すべて既読にする」
  • 各デバイスのプッシュ購読をプロフィールに一覧表示
  • Telegramボットはトークンを設定するまでオフ。ワンタイムコードで連携し、プロダクト独自のコマンドは1ファイルで追加できます
通知ページ。新しいグループオーナー、ロールの変更、招待についての未読のお知らせが3件あり、各行から該当する内容を開けます。

バックグラウンド処理

クラッシュしても失われない処理

処理は実行前に保存されるので、サーバーが途中で再起動してもメールや通知はきちんと送られます。独自のジョブも、即時、あとで、またはスケジュールに沿って実行できます。

  • 各コマンドは、変更と同じデータベーストランザクションでイベントを書き込みます
  • ワーカーがそのアウトボックスを処理し、メール、プッシュ、ライブ更新を送信
  • ジョブは1つの定義だけ:名前、ハンドラー、繰り返すならそのスケジュール
  • 失敗した試行は間隔を広げながら再試行。オペレーターは管理画面ですべての実行を確認できます
1つのトランザクション:両方保存されるか、どちらも保存されないか変更メモ、メンバーなどそのイベントアウトボックスの1行ワーカーアウトボックスを処理メールコード、お知らせプッシュスマホ、デスクトップライブほかのタブ

あなたのプロダクト

既存の機能をコピーして、新しい機能を追加

メモ機能は、データベース、API、画面、オフライン、テストまで、すべての層がそろった完全な機能です。新しい機能は、各ファイルを順番に挙げたリストに沿って、それをファイルごとにコピーして作ります。

  • どの機能も同じ形なので、コードを探しやすいまま保てます
  • 各層には、一緒にコピーできる専用のテストがあります
  • IDはすべて時系列順のUUID(バージョン7)
1 libs/domain/notes/+lib.ts 2 libs/server/db/migrations/…_notes.sql 3 libs/server/notes/ 4 apps/api/features/notes/handlers.ts 5 apps/api/features/notes/operations.ts 6 libs/ui/notes-screen.tsx 7 apps/spa/src/state/notes.ts 8 e2e/notes.e2e.ts

運用

動かして、見守って、ユーザーをサポートする

借りたサーバー1台、その動きを把握するためのツール、そしてオペレーターがプロダクトのユーザーを手助けするための管理画面。

現在使えるもの以下のモジュールはすべて現在提供中です。まだないものは各モジュールの下に記載しています。

サポート

オペレーターのための管理画面

オペレーターは名前やメールアドレスでユーザーを検索し、そのログイン方法、セッション、グループ、履歴を確認できます。同じ場所から、グループの管理、返金、ニュースレター購読者の管理、すべてのバックグラウンドジョブの確認、失敗したジョブの再実行、システムの状態確認ができます。 59秒の動画で見る.

  • 専用のホストで動く独立したアプリで、セッションCookieも別
  • ダッシュボードからAPIトークンまで、13個のモジュール
  • リスクのある操作は事前に確認を求め、すべての操作を監査ログに記録
まだないもの
  • ユーザーとしてログインし、その人と同じ画面を見る機能

デプロイ

サーバー1台、コマンド1つ

Seedは借りたLinuxサーバーならどこでも動き、開発でも本番でもDocker Composeを使います。複数のプロダクトで1台のサーバーを共有できます。

  • 前段に証明書を自動取得するTraefik
  • アプリと並んで動くPostgresとValkey
  • S3互換のファイルストレージGarageに、メモの画像やPDF、プロフィール写真を保存。アップロードは署名付きリンク経由で、中身でチェックします。写真(HEICを含む)は、位置情報などのメタデータを取り除いた小さなAVIFとWebPに変換されます
  • VictoriaMetrics、VictoriaLogs、Grafanaとアラート
  • オプトインで毎日取れる、データベースの暗号化resticバックアップと、文書化された復元手順
  • バージョンタグをプッシュすると、CIからリリースイメージを公開
$ deno task deploy Copying files... Copying infra/envs/.env.prod to infra/envs/.env... Creating web push keys if missing... Starting the app... Deploy completed.
一部省略:各ステップの間のrsyncとComposeの出力は割愛しています。
  • Traefik
  • Postgres
  • Valkey
  • VictoriaMetrics
  • VictoriaLogs
  • Grafana
  • Alertmanager
  • Garage file store
  • restic backups

Webサイト

自分用に手を加えられる公開Webサイト

このサイトのことです。高速で、サーバーでレンダリングされ、JavaScriptがオフでも使えます。料金ページ、法務ページ、ブログ、ニュースレター付き。

  • サーバー側はFresh。すべての操作が本物のリンクかフォーム
  • 料金はアプリのプランカタログから読み込み
  • ダブルオプトイン、ワンクリック購読解除、受信者の言語で届くメールに対応したニュースレター
  • 管理画面で書けるブログ。言語ごとの記事とその翻訳、トピック、RSSフィード、記事の構造化データに対応
  • 連絡先リンクは管理画面で設定し、フッターと構造化データに表示
  • ショップ用のオプションのカタログを管理画面で編集:写真、カラー、サイズ、サイズ表、売り切れのサイズを持つアイテムを、販売するすべての言語で。アイテムと共有先ごとに短いトラッキングリンクも作れます
  • sitemap.xml、robots.txt、canonicalリンク、Open Graph画像
  • 自分のUmamiを使ったCookie不要のアクセス解析(任意)。すべてのCTAにイベントを付け、管理画面の統計ページで、ソース別・アイテム別の訪問数と注文クリック数を確認できます
  • すべてのページが9言語に対応し、それぞれ専用のアドレスとhreflangリンクを持ちます。法務ページは英語のままです

品質

大事なところをテスト

2026/10/16時点で、アプリのブラウザテストスイートが34個、加えてユニットテストと画面テストがあり、すべてCIで実行されます。すべての変更は、プルリクエストとレビューを経てからマージされます。

  • Playwrightによるブラウザテストを、本物のAPIとデータベースに対して実行
  • ユニットテストと画面テストは、テスト対象のコードのすぐ隣に
  • Postgresに対する結合テスト
Seedとほかのスターターの比較

よくある質問

Seedでこんなことはできる?

SeedはVercelやサーバーレス関数で動きますか?

いいえ。Seedは、借りたLinuxサーバー1台の上でDocker Composeによる常駐コンテナとして動きます。APIがWebSocket接続を開いたままにし、ワーカーがキューを処理するためです。

SeedはNode.jsやBunで動きますか?

現時点では動きません。SeedのサーバーはDocker内のDeno 2で動き、タスクとテストにもDenoを使っています。コードはfetch、Request、Web CryptoなどのWeb標準の上に作られています。

Postgres以外のデータベースを使えますか?

いいえ。SeedはPostgresを前提に作られていて、マイグレーション、クエリ、結合テストもPostgresを想定しています。

Seedをモバイルアプリやデスクトップアプリとして配布できますか?

ストアで配布するアプリとしてはまだです。現在、Webアプリはスマホやデスクトップのブラウザからインストールでき、ネットワークなしでも開けます。Androidアプリとデスクトップアプリは計画中です。

1分で動くところを見てみましょう

デモは、初日から手に入るアプリそのものです。登録なしで開けて、ゲストアカウントには最初からサンプルデータが入っています。