Skip to content

Add fizzy signup command - #71

Merged
jeremy merged 20 commits into
masterfrom
add-signup-command
Mar 6, 2026
Merged

Add fizzy signup command#71
jeremy merged 20 commits into
masterfrom
add-signup-command

Conversation

@robzolkos

@robzolkos robzolkos commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds fizzy signup interactive wizard for humans and step-by-step subcommands (start, verify, complete) that LLM agents can drive programmatically
  • Documents the signup commands in SKILL.md for agent discoverability
  • Companion change: JSON response added to My::AccessTokensController#create in the fizzy Rails app (Add JSON response format to access token creation fizzy#2662)

How it works

The signup flow uses session-based auth (cookies) since the user doesn't have an API token yet — that's what we're generating.

Interactive wizard

fizzy signup

Walks through: hosting type → email → magic link code → name (if new) → account selection → token saved to config.

Step-by-step subcommands (for agents)

Each step returns the signed cookie values needed for the next step:

# 1. Request magic link
fizzy signup start --email user@example.com
# → {"pending_authentication_token": "eyJ...", "code": "ABC123"}
#    (code only present against development servers)

# 2. User checks email, then verify the code
fizzy signup verify --code ABC123 --pending-token eyJ...
# → {"session_token": "eyJ...", "requires_signup_completion": true/false, "accounts": [...]}

# 3a. New user — complete signup (session token via stdin)
echo "eyJ..." | fizzy signup complete --name "Full Name"

# 3b. Existing user — generate token for an account
echo "eyJ..." | fizzy signup complete --account SLUG
# → {"token": "fizzy_...", "account": "slug"}

Cookie signing

The tokens passed between steps are actual Rails signed cookie values extracted from the HTTP response jar — not the raw JSON body values. Rails signs them on the server, the CLI passes them opaquely, and Rails unsigns them on the next request. The CLI never needs to know the signing secret.

Session token security

The session token (step 2 → step 3) is read from stdin rather than a CLI flag. This keeps it out of shell history and ps output. When stdin is a terminal, the CLI prompts with masked input; when stdin is a pipe, it reads silently.

Token replay analysis

Token Visible in shell history / ps? Replayable? Details
pending_authentication_token Yes (--pending-token flag) No. The magic link code it's paired with is destroyed on first use (MagicLink#consume calls destroy). The token alone just contains a signed email — useless without an unconsumed code. Expires with the magic link (15 min).
session_token No. Read from stdin. Yes — it's a signed_id of a persistent Session DB record. Not exposed on the command line. In a pipe, only exists in process memory. In a terminal, entered via masked prompt.
fizzy_... access token No. Only in JSON output, saved to config file. Yes — this is the permanent API credential. Never passed between steps. Stored in ~/.config/fizzy/config.yaml.

Copilot AI review requested due to automatic review settings March 4, 2026 17:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new fizzy signup command to support session/cookie-based onboarding before a user has an API token, including an interactive wizard and agent-friendly step-by-step subcommands. Updates the Fizzy skill documentation so agents can discover and drive the flow.

Changes:

  • Add fizzy signup interactive wizard plus signup start|verify|complete subcommands for programmatic signup/token generation.
  • Implement cookie-jar based HTTP helpers to carry Rails signed cookies between steps.
  • Document the new signup flow in skills/fizzy/SKILL.md.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.

File Description
skills/fizzy/SKILL.md Documents interactive and step-by-step signup usage for agents.
internal/commands/signup.go Implements signup wizard + step-by-step commands, cookie handling, and config persistence.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go Outdated
Comment thread skills/fizzy/SKILL.md
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go Outdated
@robzolkos
robzolkos marked this pull request as draft March 4, 2026 18:49

@jeremy jeremy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can any of the tokens that'll land on the command line be replayed/reused? Should treat them as credentials and securely read from stdin if so.

@robzolkos
robzolkos force-pushed the add-signup-command branch from 627609c to 66692ac Compare March 5, 2026 15:28
Copilot AI review requested due to automatic review settings March 5, 2026 15:28
@github-actions github-actions Bot added the enhancement New feature or request label Mar 5, 2026
Comment thread internal/commands/signup.go Fixed
Comment thread internal/commands/signup.go Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go
Comment thread skills/fizzy/SKILL.md
Interactive wizard (fizzy signup) and step-by-step subcommands
(start, verify, complete) for programmatic/agent use.

Session token is read from stdin to avoid exposing credentials
in shell history or process arguments.
@robzolkos
robzolkos force-pushed the add-signup-command branch from 66692ac to 5fe0a95 Compare March 5, 2026 16:29
Matches the pattern used by `fizzy setup`. Returns a usage hint
directing agents to the step-by-step subcommands instead.
Copilot AI review requested due to automatic review settings March 5, 2026 16:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Two bugs found during local testing:
- Interactive wizard discarded the HTTP response, so the X-Magic-Link-Code
  header (set by Rails in development) was never shown to the user.
- Token was saved only to config YAML, but the credstore takes precedence,
  so a stale keyring token would shadow the newly generated one.
- Return http.Header instead of *http.Response from signupPost to satisfy bodyclose
- Use http.NewRequestWithContext instead of http.NewRequest (noctx)
- Check MarkFlagRequired and huh.Confirm.Run error returns (errcheck/gosec)
- Fix variable shadowing of err in nested blocks (govet)
- Clean expanded path with filepath.Clean to prevent path injection
- Sanitize path output to prevent log injection
Copilot AI review requested due to automatic review settings March 5, 2026 17:57
Comment thread internal/commands/skill.go Fixed
@flavorjones

Copy link
Copy Markdown
Member

I like the stdin for the session token, but I do think we need to specify in the skill that the token should be written to a temp file (and cleaned up after signup) in order to keep it out of the agent session.

Write the session token to a temp file and pipe via stdin instead of
echoing directly, keeping it out of the agent's conversation context.
- Resolve paths with filepath.Abs to sanitize user input (path injection)
- Strip control characters from output to prevent log injection
Comment thread internal/commands/skill.go Fixed
@robzolkos
robzolkos marked this pull request as ready for review March 5, 2026 18:46
@robzolkos
robzolkos requested a review from jeremy March 5, 2026 18:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jeremy jeremy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage gaps:

  • hosted signup clears previously saved self-hosted URL
  • self-hosted signup preserves custom URL
  • signupHTTPError for auth failures
  • signupHTTPError for server errors
  • plain error for network failures

Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/skill.go Outdated
Comment thread internal/commands/skill.go Outdated
jeremy added 10 commits March 5, 2026 15:14
The overwrite prompt and progress line still emitted the raw
selectedPath, allowing newline/control-character injection via
custom path input. Apply sanitizeLogValue consistently at all
three output sinks.
os.Exit bypasses deferred cleanup and is untestable. Match the
established setup.go convention of returning nil with nolint:nilerr
when the user cancels a huh prompt.
The subcommand path (signupAPIURL) already trims trailing slashes,
but the interactive wizard did not. Entering https://fizzy.example.com/
would produce double-slash URLs like //session.json.
The wizard's verify loop previously treated every failure as "Invalid
code" and offered retry. Network errors, 5xx responses, and other
operational failures now surface the real error immediately instead
of misleading the user. Only 401 (wrong code) triggers the retry
prompt.

Introduces signupHTTPError to carry the HTTP status code from
signupPost, letting callers discriminate by status.
saveSignupConfig only wrote APIURL when non-default, so a previously
saved self-hosted URL survived a hosted signup. The fresh token and
account would then be used against the wrong backend. Now explicitly
clears APIURL when the signup used the default hosted URL.
The token is already sent as a cookie (set on the line above). The
interactive wizard path only sends {"code": ...} in the body. Make
the programmatic path consistent.
Restrict http:// URLs to loopback addresses only (localhost, 127.0.0.1,
[::1]) and reject non-HTTP schemes entirely. Validation runs on every
request URL in signupGet/signupPost and on every redirect hop via
CheckRedirect, closing the open-redirector bypass.
- Cap response body reads at 1MB via io.LimitReader
- Return *signupHTTPError from signupGet (consistent with signupPost)
- Handle url.Parse errors in cookie helpers instead of ignoring
- Set Secure and HttpOnly on synthesized cookies
- Use url.PathEscape on account slugs in URL construction
Copilot AI review requested due to automatic review settings March 5, 2026 23:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/fizzy/SKILL.md Outdated
Comment thread internal/skills/SKILL.md Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go Outdated
Comment thread internal/commands/signup.go
Comment thread internal/commands/signup_test.go
- Set Secure cookie flag conditionally based on URL scheme so http://
  localhost works for local development
- Remove hardcoded ~/.config/fizzy/config.yaml from output messages
- Document credential store vs config file fallback in SKILL.md
- Close pipe reader FD in test cleanup to prevent FD leak
@jeremy
jeremy merged commit c2ef2e5 into master Mar 6, 2026
15 checks passed
@jeremy
jeremy deleted the add-signup-command branch March 6, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants