Rename to uncensored-send

This commit is contained in:
2026-09-13 08:57:01 +02:00
parent eaa0a5d1d7
commit 7d0d303c2a
21 changed files with 61 additions and 61 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
/send
/uncensored-send
/data/
+4 -4
View File
@@ -13,8 +13,8 @@ Built for sharing large binaries with friends so that I don't have to manually m
```
go build
./send token add me --vanity --admin
./send
./uncensored-send token add me --vanity --admin
./uncensored-send
```
Open <http://localhost:8080>, click **Log in**, paste the token. That's it.
@@ -25,9 +25,9 @@ upload limit.
## Options
**Read `./send --help` rather than this file.**
**Read `./uncensored-send --help` rather than this file.**
Every option also reads from `SEND_`-prefixed environment variables.
Every option also reads from `UNCENSORED_SEND_`-prefixed environment variables.
## Development
+1 -1
View File
@@ -1,3 +1,3 @@
module send
module uncensored-send
go 1.25
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"sync"
"time"
"send/internal/config"
"uncensored-send/internal/config"
)
// tokenFilePerm is deliberately stricter than the rest of the data directory:
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"testing"
"time"
"send/internal/config"
"uncensored-send/internal/config"
)
func defaults() *config.Config {
+1 -1
View File
@@ -35,7 +35,7 @@ type Config struct {
trustedProxyNets []*net.IPNet
}
const EnvPrefix = "SEND_"
const EnvPrefix = "UNCENSORED_SEND_"
// Register wires every option onto s. Short forms exist only for the options
// reached often; everything else is long-only, by design.
+4 -4
View File
@@ -86,7 +86,7 @@ func TestFormatRoundTrips(t *testing.T) {
func TestHyphenConvention(t *testing.T) {
newSet := func() (*Set, *string) {
var listen string
s := NewSet("test", "SEND_TEST_")
s := NewSet("test", "UNCENSORED_SEND_TEST_")
s.String(&listen, "listen", "l", "default", "ADDR", "listen address")
return s, &listen
}
@@ -116,9 +116,9 @@ func TestHyphenConvention(t *testing.T) {
}
func TestEnvFallback(t *testing.T) {
t.Setenv("SEND_TEST_MAX_SIZE", "4GiB")
t.Setenv("UNCENSORED_SEND_TEST_MAX_SIZE", "4GiB")
var size int64
s := NewSet("test", "SEND_TEST_")
s := NewSet("test", "UNCENSORED_SEND_TEST_")
s.Size(&size, "max-size", "s", "2GiB", "cap")
if err := s.Parse(nil); err != nil {
t.Fatal(err)
@@ -128,7 +128,7 @@ func TestEnvFallback(t *testing.T) {
}
// An explicit flag still wins over the environment.
s = NewSet("test", "SEND_TEST_")
s = NewSet("test", "UNCENSORED_SEND_TEST_")
s.Size(&size, "max-size", "s", "2GiB", "cap")
if err := s.Parse([]string{"--max-size", "1GiB"}); err != nil {
t.Fatal(err)
+3 -3
View File
@@ -52,7 +52,7 @@ func (s *Set) note(long, short string) {
}
}
// envName maps --max-size to SEND_MAX_SIZE.
// envName maps --max-size to UNCENSORED_SEND_MAX_SIZE.
func (s *Set) envName(long string) string {
return s.envPrefix + strings.ToUpper(strings.ReplaceAll(long, "-", "_"))
}
@@ -183,7 +183,7 @@ func (s *Set) checkConvention(args []string) error {
return nil
}
// applyEnv seeds every option from its SEND_-prefixed environment variable.
// applyEnv seeds every option from its UNCENSORED_SEND_-prefixed environment variable.
// Called before parsing so an explicit command-line flag always wins.
func (s *Set) applyEnv() error {
for _, sp := range s.specs {
@@ -214,7 +214,7 @@ func (s *Set) Parse(args []string) error {
// Changed reports whether an option was given on the command line.
//
// Values picked up from the environment do not count: a command that acts only
// on the options you actually typed must not quietly act on a SEND_ variable
// on the options you actually typed must not quietly act on a UNCENSORED_SEND_ variable
// exported for the server.
func (s *Set) Changed(long string) bool {
if s.fromEnv[long] {
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"slices"
"time"
"send/internal/config"
"uncensored-send/internal/config"
)
// adminPage is the one view that shows every object, regardless of who
+1 -1
View File
@@ -14,7 +14,7 @@ import (
// reason to prefer it over localStorage, where any injected script could
// exfiltrate the credential. The page never needs to see the value: the server
// resolves it and renders who the caller is.
const tokenCookie = "send_token"
const tokenCookie = "uncensored_send_token"
// rememberFor is how long a persisted login survives. Tokens are revoked by
// deleting them from the token file, so a long window costs nothing.
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"net/http"
"strings"
"send/internal/auth"
"send/internal/store"
"uncensored-send/internal/auth"
"uncensored-send/internal/store"
)
// handleDelete removes an object early. Three credentials are accepted: the
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"net/http"
"strings"
"send/internal/store"
"uncensored-send/internal/store"
)
// downloadCSP is as inert as a policy gets. Combined with the attachment
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"net/url"
"strings"
"send/internal/config"
"uncensored-send/internal/config"
)
// loginPage backs both the form and its error redisplay.
+3 -3
View File
@@ -6,9 +6,9 @@ import (
"net/http"
"time"
"send/internal/auth"
"send/internal/config"
"send/internal/store"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
"uncensored-send/internal/store"
)
func writeJSON(w http.ResponseWriter, status int, v any) {
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"sync"
"time"
"send/internal/config"
"uncensored-send/internal/config"
)
// limiter is a per-key token bucket. It is deliberately small: there is no
+4 -4
View File
@@ -11,10 +11,10 @@ import (
"strings"
"time"
"send/internal/auth"
"send/internal/config"
"send/internal/store"
"send/web"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
"uncensored-send/internal/store"
"uncensored-send/web"
)
// Server holds everything the handlers need. It is safe for concurrent use.
+4 -4
View File
@@ -19,9 +19,9 @@ import (
"testing"
"time"
"send/internal/auth"
"send/internal/config"
"send/internal/store"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
"uncensored-send/internal/store"
)
// clock is the fixed instant tests start from; s.now is swapped so expiry can
@@ -42,7 +42,7 @@ func newHarness(t *testing.T, tweak func(*config.Config)) *harness {
dir := t.TempDir()
cfg := config.Config{}
fs := config.NewSet("test", "SEND_TEST_")
fs := config.NewSet("test", "UNCENSORED_SEND_TEST_")
cfg.Register(fs)
if err := fs.Parse(nil); err != nil {
t.Fatal(err)
+3 -3
View File
@@ -10,9 +10,9 @@ import (
"strings"
"time"
"send/internal/auth"
"send/internal/config"
"send/internal/store"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
"uncensored-send/internal/store"
)
const (
+12 -12
View File
@@ -1,4 +1,4 @@
// Command send is a small self-hosted file drop: uploads land in a flat data
// Command uncensored-send is a small self-hosted file drop: uploads land in a flat data
// directory, expire on their own, and are served back as inert attachments.
package main
@@ -15,10 +15,10 @@ import (
"syscall"
"time"
"send/internal/auth"
"send/internal/config"
"send/internal/server"
"send/internal/store"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
"uncensored-send/internal/server"
"uncensored-send/internal/store"
)
func main() {
@@ -26,7 +26,7 @@ func main() {
if errors.Is(err, flag.ErrHelp) {
os.Exit(0)
}
fmt.Fprintln(os.Stderr, "send: "+err.Error())
fmt.Fprintln(os.Stderr, "uncensored-send: "+err.Error())
os.Exit(1)
}
}
@@ -38,13 +38,13 @@ func run(args []string) error {
return serve(args)
}
const serveUsage = `send - a small self-hosted file drop
const serveUsage = `uncensored-send - a small self-hosted file drop
Usage:
send [options] run the server
send token add <name> [opts] mint a token, printing it once
send token list list tokens
send token rm <name> remove a token
uncensored-send [options] run the server
uncensored-send token add <name> [opts] mint a token, printing it once
uncensored-send token list list tokens
uncensored-send token rm <name> remove a token
Options take one hyphen with a single letter (-s) and two with a full
word (--max-size). Every option can also be set from the environment.
@@ -54,7 +54,7 @@ Options:
func serve(args []string) error {
var cfg config.Config
fs := config.NewSet("send", config.EnvPrefix)
fs := config.NewSet("uncensored-send", config.EnvPrefix)
fs.SetOutput(os.Stderr)
cfg.Register(fs)
+11 -11
View File
@@ -12,19 +12,19 @@ import (
"text/tabwriter"
"time"
"send/internal/auth"
"send/internal/config"
"uncensored-send/internal/auth"
"uncensored-send/internal/config"
)
const tokenUsage = `send token - manage upload credentials
const tokenUsage = `uncensored-send token - manage upload credentials
Usage:
send token add <name> [options] generate a token
send token add <name> --token - read a chosen one from stdin
send token rotate <name> [--token -] replace the secret, keep everything else
send token set <name> [options] change limits and flags in place
send token list [options]
send token rm <name> [options]
uncensored-send token add <name> [options] generate a token
uncensored-send token add <name> --token - read a chosen one from stdin
uncensored-send token rotate <name> [--token -] replace the secret, keep everything else
uncensored-send token set <name> [options] change limits and flags in place
uncensored-send token list [options]
uncensored-send token rm <name> [options]
A token grants its own size and lifetime limits. Any limit left unset is
inherited from the running server's defaults, so a token with no options
@@ -136,7 +136,7 @@ type tokenOptions struct {
}
func (o *tokenOptions) register() *config.Set {
fs := config.NewSet("send token", config.EnvPrefix)
fs := config.NewSet("uncensored-send token", config.EnvPrefix)
fs.String(&o.dataDir, "data", "d", "./data", "DIR", "directory holding the data")
fs.String(&o.tokensPath, "tokens", "", "", "FILE", "token file location (default <data>/tokens.json)")
fs.String(&o.maxSize, "max-size", "s", "", "SIZE", "per-upload cap for this token; 'unlimited' to remove it")
@@ -296,7 +296,7 @@ func tokenSet(file *auth.File, name string, opts tokenOptions, fs *config.Set) e
changes = append(changes, fmt.Sprintf("--%s=%t", f.flag, f.value))
}
if fs.Changed("token") {
return errors.New("use \"send token rotate\" to change the secret")
return errors.New("use \"uncensored-send token rotate\" to change the secret")
}
return nil
})
+1 -1
View File
@@ -52,7 +52,7 @@
<section class="card">
<h2>Tokens</h2>
<p class="hint">
Tokens are minted, rotated and revoked with <code>./send token</code> on the
Tokens are minted, rotated and revoked with <code>./uncensored-send token</code> on the
server. They are deliberately not manageable from here: nothing that hands
out credentials should be reachable over the network.
</p>