Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 16 additions & 4 deletions crates/buzz-admin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use buzz_core::tenant::{relay_url_authority, TenantContext};
use buzz_db::{Db, DbConfig};
use buzz_pubsub::{EventTopic, PubSubManager};
use clap::{Parser, Subcommand};
use nostr::{EventBuilder, Keys, Kind, Tag};
use nostr::{EventBuilder, Keys, Kind, Tag, ToBech32};
use tracing::warn;

#[derive(Parser)]
Expand Down Expand Up @@ -131,9 +131,21 @@ async fn run(cli: Cli) -> Result<i32> {
match cli.command {
Command::GenerateKey => {
let keys = Keys::generate();
println!("Public key: {}", keys.public_key().to_hex());
println!("Secret key: {}", keys.secret_key().display_secret());
println!("\nSet BUZZ_PRIVATE_KEY to the secret key to use this identity.");
let npub = keys
.public_key()
.to_bech32()
.map_err(|e| anyhow::anyhow!("encode npub: {e}"))?;
let nsec = keys
.secret_key()
.to_bech32()
.map_err(|e| anyhow::anyhow!("encode nsec: {e}"))?;
println!("Public key (hex): {}", keys.public_key().to_hex());
println!("Public key (npub): {npub}");
println!("Secret key (hex): {}", keys.secret_key().display_secret());
println!("Secret key (nsec): {nsec}");
println!(
"\nSet BUZZ_PRIVATE_KEY to the hex or nsec secret. Desktop onboarding accepts nsec."
);
Ok(0)
}
Command::Migrate => {
Expand Down
4 changes: 4 additions & 0 deletions deploy/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ keypair.
`<bucket>.minio`. It is not configurable for an external S3 provider through
`.env`; use the Helm chart or a custom Compose configuration for providers
such as new Railway Storage Buckets that require `virtual` addressing.
- `git-data-init` chowns the `buzz-git-data` volume to uid/gid `1000` before
the relay starts. Fresh Docker named volumes are root-owned; without this
step the non-root relay cannot create `/data/git/.pack-cache` and
restart-loops.

Run `./run.sh backup-hint` for the backup checklist.

Expand Down
15 changes: 15 additions & 0 deletions deploy/compose/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ services:
condition: service_healthy
minio-init:
condition: service_completed_successfully
git-data-init:
condition: service_completed_successfully
# Probe /_readiness over /dev/tcp because the runtime image has bash but no curl/wget/socat.
healthcheck:
test:
Expand Down Expand Up @@ -121,6 +123,19 @@ services:
networks:
- buzz-net

# Named volumes are root-owned on first create; the relay runs as uid 1000
# (Dockerfile USER buzz) and must create BUZZ_GIT_PACK_CACHE_PATH under
# /data/git. Mirror the minio-init pattern so a fresh stack reaches healthy.
git-data-init:
image: busybox:1.37.0
user: "0:0"
volumes:
- buzz-git-data:/data/git
command: ["sh", "-ec", "chown -R 1000:1000 /data/git"]
restart: "no"
networks:
- buzz-net

volumes:
buzz-postgres-data:
labels:
Expand Down
81 changes: 77 additions & 4 deletions desktop/src/features/communities/ui/WelcomeSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import { Button } from "@/shared/ui/button";
import { Card } from "@/shared/ui/card";
import { StartupWindowDragRegion } from "@/shared/ui/StartupWindowDragRegion";

type WelcomeSetupPage = "welcome" | "existing" | "join" | "member" | "owned";
type WelcomeSetupPage =
| "welcome"
| "existing"
| "owner"
| "join"
| "member"
| "owned";
type WelcomeTransitionMode = "initial" | OnboardingTransitionDirection;

type WelcomeSetupProps = {
Expand All @@ -41,6 +47,11 @@ export function WelcomeSetup({
const [page, setPage] = React.useState<WelcomeSetupPage>(initialPage);
const [transitionMode, setTransitionMode] =
React.useState<WelcomeTransitionMode>(initialTransitionMode);
// Where "member" (relay URL) was entered from, so Back returns correctly
// for both "I'm a member" and "Connect a self-hosted relay".
const [memberBackPage, setMemberBackPage] = React.useState<
"existing" | "owner" | "welcome"
>("existing");
// While true, the Builderlab sign-in modal floats over the current page —
// we only navigate to the hosted stage once sign-in completes, so the page
// behind the modal never changes out from under the user.
Expand Down Expand Up @@ -199,7 +210,7 @@ export function WelcomeSetup({
>
<button
data-testid="existing-choice-owner"
onClick={() => setIsHostedSignInOpen(true)}
onClick={() => showPage("owner")}
type="button"
>
I own the community
Expand All @@ -212,7 +223,10 @@ export function WelcomeSetup({
>
<button
data-testid="existing-choice-member"
onClick={() => showPage("member")}
onClick={() => {
setMemberBackPage("existing");
showPage("member");
}}
type="button"
>
I’m a member or admin
Expand All @@ -231,6 +245,65 @@ export function WelcomeSetup({
</Button>
</OnboardingFooter>
</OnboardingSlideTransition>
) : page === "owner" ? (
<OnboardingSlideTransition
className="flex h-full min-h-0 w-full flex-col items-center text-center"
containerClassName="h-full min-h-0 [&>.buzz-onboarding-transition-line]:h-full"
direction={transitionDirection}
transitionKey={`owner-${transitionDirection}`}
>
<div className="w-full max-w-[760px]">
<h1 className="text-title font-normal">
Connect the community you own
</h1>
<p className="mt-3 text-sm leading-6 text-foreground/80">
Self-hosted relays connect by URL. Hosted communities use
Builderlab sign-in.
</p>
</div>
<div className="flex w-full flex-1 translate-y-16 flex-col items-center justify-center gap-20 py-8">
<Card
asChild
className={COMMUNITY_OPTION_CARD_CLASS}
variant="textured"
>
<button
data-testid="owner-choice-self-hosted"
onClick={() => {
setMemberBackPage("owner");
showPage("member");
}}
type="button"
>
Connect a self-hosted relay
</button>
</Card>
<Card
asChild
className={COMMUNITY_OPTION_CARD_CLASS}
variant="textured"
>
<button
data-testid="owner-choice-hosted"
onClick={() => setIsHostedSignInOpen(true)}
type="button"
>
Sign in with Builderlab
</button>
</Card>
</div>
<OnboardingFooter>
<Button
className="h-9 rounded-full bg-foreground/10 px-6 hover:bg-foreground/15"
data-testid="owner-back"
onClick={() => showPage("existing")}
type="button"
variant="ghost"
>
Back
</Button>
</OnboardingFooter>
</OnboardingSlideTransition>
) : page === "owned" ? (
<OnboardingSlideTransition
className="flex w-full flex-col items-center text-center"
Expand Down Expand Up @@ -262,7 +335,7 @@ export function WelcomeSetup({
error={null}
isRedeeming={false}
onCancel={() =>
showPage(page === "member" ? "existing" : "welcome")
showPage(page === "member" ? memberBackPage : "welcome")
}
onConnect={startConnection}
onRedeem={redeemInvite}
Expand Down