4 Commits

Author SHA1 Message Date
clawbot
5ad2c37ba2 refactor: move CLI code from cmd/ to internal/cli/
All checks were successful
check / check (push) Successful in 4s
Move all substantive CLI code (app logic, UI, API client,
hashcash, types) from cmd/neoirc-cli/ to internal/cli/.
The cmd/neoirc-cli/main.go now contains only minimal
bootstrapping that calls cli.Run().

This follows the project convention that cmd/ should only
contain minimal main() bootstrapping code.
2026-03-10 03:29:57 -07:00
2a3d2dc94c Merge branch 'main' into feat/hashcash-pow
All checks were successful
check / check (push) Successful in 1m3s
2026-03-10 11:21:21 +01:00
clawbot
ff9a943e6d fix: move hashcash PoW from build artifact to JSX source
All checks were successful
check / check (push) Successful in 2m20s
The hashcash proof-of-work implementation was incorrectly added to the
build artifact web/dist/app.js instead of the source file web/src/app.jsx.
Running web/build.sh would overwrite all hashcash changes.

Changes:
- Add checkLeadingZeros() and mintHashcash() functions to app.jsx
- Integrate hashcash into LoginScreen: fetch hashcash_bits from /server,
  compute stamp via Web Crypto API before session creation, show
  'Computing proof-of-work...' feedback
- Remove web/dist/ from git tracking (build artifacts)
- Add web/dist/ to .gitignore
2026-03-10 03:12:46 -07:00
clawbot
b48e164d88 feat: implement hashcash proof-of-work for session creation
Add SHA-256-based hashcash proof-of-work requirement to POST /session
to prevent abuse via rapid session creation. The server advertises the
required difficulty via GET /server (hashcash_bits field), and clients
must include a valid stamp in the X-Hashcash request header.

Server-side:
- New internal/hashcash package with stamp validation (format, bits,
  date, resource, replay prevention via in-memory spent set)
- Config: NEOIRC_HASHCASH_BITS env var (default 20, set 0 to disable)
- GET /server includes hashcash_bits when > 0
- POST /session validates X-Hashcash header when enabled
- Returns HTTP 402 for missing/invalid stamps

Client-side:
- SPA: fetches hashcash_bits from /server, computes stamp using Web
  Crypto API with batched SHA-256, shows 'Computing proof-of-work...'
  feedback during computation
- CLI: api package gains MintHashcash() function, CreateSession()
  auto-fetches server info and computes stamp when required

Stamp format: 1:bits:YYMMDD:resource::counter (standard hashcash)

closes #11
2026-03-10 03:11:37 -07:00
3 changed files with 21 additions and 20 deletions

View File

@@ -2311,18 +2311,15 @@ neoirc/
├── cmd/
│ ├── neoircd/ # Server binary entry point
│ │ └── main.go
│ └── neoirc-cli/ # TUI client entry point
── main.go # Minimal bootstrapping (calls internal/cli)
│ └── neoirc-cli/ # TUI client
── main.go # Command handling, poll loop
│ ├── ui.go # tview-based terminal UI
│ └── api/
│ ├── client.go # HTTP API client library
│ └── types.go # Request/response types
├── internal/
│ ├── broker/ # In-memory pub/sub for long-poll notifications
│ │ └── broker.go
│ ├── cli/ # TUI client implementation
│ │ ├── app.go # App struct, command handling, poll loop
│ │ ├── ui.go # tview-based terminal UI
│ │ └── api/
│ │ ├── client.go # HTTP API client library
│ │ ├── types.go # Request/response types
│ │ └── hashcash.go # Hashcash proof-of-work minting
│ ├── config/ # Viper-based configuration
│ │ └── config.go
│ ├── db/ # Database access and migrations

View File

@@ -1,8 +1,17 @@
// Package main is the entry point for the neoirc-cli client.
package main
import "git.eeqj.de/sneak/neoirc/internal/cli"
import (
"fmt"
"os"
"git.eeqj.de/sneak/neoirc/internal/cli"
)
func main() {
cli.Run()
err := cli.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}

View File

@@ -1,9 +1,8 @@
// Package cli implements the neoirc-cli terminal client.
// Package cli implements the neoirc-cli client logic.
package cli
import (
"fmt"
"os"
"strings"
"sync"
"time"
@@ -32,8 +31,8 @@ type App struct {
stopPoll chan struct{}
}
// Run creates and runs the CLI application.
func Run() {
// Run starts the neoirc-cli application.
func Run() error {
app := &App{ //nolint:exhaustruct
ui: NewUI(),
nick: "guest",
@@ -51,11 +50,7 @@ func Run() {
"or [yellow]/help[white] for commands",
)
err := app.ui.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
return app.ui.Run()
}
func (a *App) handleInput(text string) {