1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! CLI definitions via clap. Commands: `serve`, `publish`, `list`, `delete`, `token`, `mcp`, `audit`.
use clap::{Parser, Subcommand};
/// Dual-layer markdown share service.
///
/// Serves two views from one document: a styled human view and a full raw
/// agent view. POST markdown in, get a URL out.
#[derive(Parser, Debug)]
#[command(name = "twofold", version, about)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Start the HTTP server.
///
/// Reads configuration from environment variables:
/// TWOFOLD_TOKEN (required) Bearer token for publish auth
/// TWOFOLD_BIND (optional) Bind address (default: 127.0.0.1:3000)
/// TWOFOLD_DB_PATH (optional) SQLite path (default: ./twofold.db)
/// TWOFOLD_BASE_URL (optional) Base URL (default: http://localhost:3000)
/// TWOFOLD_MAX_SIZE (optional) Max body bytes (default: 1048576)
/// TWOFOLD_REAPER_INTERVAL (optional) Reaper interval seconds (default: 60)
/// TWOFOLD_DEFAULT_THEME (optional) Default theme (default: clean)
/// TWOFOLD_WEBHOOK_URL (optional) Webhook endpoint URL
/// TWOFOLD_WEBHOOK_SECRET (optional) HMAC signing secret for webhooks
Serve,
/// Publish a markdown document to a twofold server.
///
/// Reads the file at PATH (or stdin if PATH is `-`) and POSTs it to the
/// server. Prints the resulting URL to stdout on success. Exits 1 on failure.
Publish(PublishArgs),
/// List published documents on a twofold server.
List(ListArgs),
/// Delete a document by slug.
Delete(DeleteArgs),
/// Manage API tokens.
Token(TokenArgs),
/// View recent audit log entries.
Audit(AuditArgs),
/// Start the MCP server (stdio JSON-RPC).
///
/// Reads:
/// TWOFOLD_MCP_SERVER Server URL (default: http://localhost:3000)
/// TWOFOLD_MCP_TOKEN Bearer token (falls back to TWOFOLD_TOKEN)
Mcp,
}
/// Arguments for the `audit` subcommand.
#[derive(clap::Args, Debug)]
pub struct AuditArgs {
/// Server base URL.
#[arg(long, default_value = "http://localhost:3000")]
pub server: String,
/// Bearer token for authentication.
/// Defaults to the TWOFOLD_TOKEN environment variable.
#[arg(long)]
pub token: Option<String>,
/// Maximum number of entries to show.
#[arg(long, default_value = "20")]
pub limit: u32,
}
/// Arguments for the `publish` subcommand.
#[derive(clap::Args, Debug)]
pub struct PublishArgs {
/// Path to the markdown file to publish, or `-` to read from stdin.
pub path: String,
/// Server base URL.
#[arg(long, default_value = "http://localhost:3000")]
pub server: String,
/// Bearer token for authentication.
/// Defaults to the TWOFOLD_TOKEN environment variable.
#[arg(long)]
pub token: Option<String>,
/// Document title (prepended as frontmatter).
#[arg(long)]
pub title: Option<String>,
/// Custom slug (prepended as frontmatter).
#[arg(long)]
pub slug: Option<String>,
/// Theme (clean, dark, paper, minimal).
#[arg(long)]
pub theme: Option<String>,
/// Expiry duration (e.g., 7d, 24h, 30m, 2w).
#[arg(long)]
pub expiry: Option<String>,
/// Password to protect the document.
#[arg(long)]
pub password: Option<String>,
}
/// Arguments for the `list` subcommand.
#[derive(clap::Args, Debug)]
pub struct ListArgs {
/// Server base URL.
#[arg(long, default_value = "http://localhost:3000")]
pub server: String,
/// Bearer token for authentication.
/// Defaults to the TWOFOLD_TOKEN environment variable.
#[arg(long)]
pub token: Option<String>,
/// Maximum number of documents to show.
#[arg(long, default_value = "20")]
pub limit: u32,
}
/// Arguments for the `delete` subcommand.
#[derive(clap::Args, Debug)]
pub struct DeleteArgs {
/// Slug of the document to delete.
pub slug: String,
/// Server base URL.
#[arg(long, default_value = "http://localhost:3000")]
pub server: String,
/// Bearer token for authentication.
/// Defaults to the TWOFOLD_TOKEN environment variable.
#[arg(long)]
pub token: Option<String>,
}
/// Arguments for the `token` subcommand.
#[derive(clap::Args, Debug)]
pub struct TokenArgs {
#[command(subcommand)]
pub action: TokenAction,
}
#[derive(Subcommand, Debug)]
pub enum TokenAction {
/// Create a new API token.
Create {
/// Human-readable name for the token.
#[arg(long)]
name: String,
/// Path to the SQLite database.
/// Defaults to TWOFOLD_DB_PATH or ./twofold.db.
#[arg(long)]
db: Option<String>,
},
/// List all tokens.
List {
/// Path to the SQLite database.
#[arg(long)]
db: Option<String>,
},
/// Revoke a token by name.
Revoke {
/// Name of the token to revoke.
#[arg(long)]
name: String,
/// Path to the SQLite database.
#[arg(long)]
db: Option<String>,
},
}