posthorn 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -58
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,19 +42,64 @@ The platform is a pure orchestration layer: the USER owns their Cloudflare
|
|
|
42
42
|
account, their Google Workspace, their domains, and their mailboxes. Posthorn
|
|
43
43
|
never owns or bills for any of it.
|
|
44
44
|
|
|
45
|
-
## The
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
## The flow: walk it like a setup wizard
|
|
46
|
+
|
|
47
|
+
Go step by step. Ask one question at a time, only when the step needs it, and
|
|
48
|
+
do as much as possible yourself. Don't front-load questions.
|
|
49
|
+
|
|
50
|
+
Step 1. `posthorn auth register`. Instant API key; no email or signup info
|
|
51
|
+
needed, and there are no account tiers. Nothing to ask.
|
|
52
|
+
|
|
53
|
+
Step 2. Domain. Ask: "Do you need a new domain for your emails, or will you
|
|
54
|
+
use an existing one for sending?"
|
|
55
|
+
- NEW domain (the purchase always happens on the user's own Cloudflare account
|
|
56
|
+
and card; Posthorn never owns or bills for domains):
|
|
57
|
+
a. They need a Cloudflare account with a payment method (they pay Cloudflare
|
|
58
|
+
directly for the domain, ~$10/yr). Browser step: create the API token
|
|
59
|
+
(below), then `posthorn accounts cloudflare <token>`.
|
|
60
|
+
b. `posthorn domains check name1.com name2.io --cloudflare <id>` (availability
|
|
61
|
+
+ price) → `posthorn domains reputation <pick>` (vets Spamhaus blocklist,
|
|
62
|
+
reputation score, and prior use; verdicts: clean / caution / avoid. Run it
|
|
63
|
+
before buying; you can't tell a tainted domain from its name.) →
|
|
64
|
+
`posthorn domains buy <pick> --cloudflare <id> --contact '{...}'`
|
|
65
|
+
- EXISTING domain. If it runs their live website or real email, recommend a
|
|
66
|
+
dedicated sending domain instead. Two ways to connect it; either way the
|
|
67
|
+
domain stays registered where they bought it, only nameservers change:
|
|
68
|
+
a. Their own Cloudflare: they manage the domain's DNS in their own Cloudflare
|
|
69
|
+
account. Needs the API token (below); and if the domain isn't on their
|
|
70
|
+
Cloudflare yet, they add it at dash.cloudflare.com and point nameservers
|
|
71
|
+
there first. Then `posthorn domains connect <domain> --cloudflare <id>`.
|
|
72
|
+
b. Posthorn-managed DNS: no Cloudflare account to create or manage; Posthorn
|
|
73
|
+
hosts the domain's DNS for them (free). `posthorn domains managed <domain>`
|
|
74
|
+
→ they set the 2 printed nameservers at their registrar →
|
|
75
|
+
`posthorn domains activate <id>`. Note: Posthorn then hosts ALL of the
|
|
76
|
+
domain's DNS records (website records included), so it fits domains
|
|
77
|
+
dedicated to email.
|
|
78
|
+
|
|
79
|
+
Step 3. Google Workspace. Ask: "Do you have a Google Workspace you can add new
|
|
80
|
+
domains and mailboxes to, or do you want to set up a new one?"
|
|
81
|
+
- Existing Workspace: delegation browser step (below), then
|
|
82
|
+
`posthorn accounts workspace <admin-email>`.
|
|
83
|
+
- New Workspace: they create one at workspace.google.com using the domain from
|
|
84
|
+
step 2, then delegation as above. Tell the user the org signup is also
|
|
85
|
+
one-time; it gets reused for every future domain and mailbox.
|
|
86
|
+
(Delegation is per org, not per domain, so with an existing org this step can
|
|
87
|
+
happen any time, even before the domain exists. The domain pipeline auto-adds
|
|
88
|
+
and verifies the new domain inside their org.)
|
|
89
|
+
|
|
90
|
+
Step 4. Wait for the domain: poll `posthorn domains get <id>` until status
|
|
91
|
+
`ready` (minutes to 24h; poll every 30-60s, don't hammer). MX/SPF/DMARC are
|
|
92
|
+
configured automatically. Statuses progress: pending_* → purchased →
|
|
93
|
+
dns_configuring → dns_configured → dns_verified → workspace_verifying →
|
|
94
|
+
workspace_verified → ready.
|
|
95
|
+
|
|
96
|
+
Step 5. DKIM. Browser step below; once per domain, needs the domain `ready`.
|
|
97
|
+
|
|
98
|
+
Step 6. `posthorn mailboxes create <domain-id> --email ... --first ... --last ...`
|
|
99
|
+
|
|
100
|
+
Step 7. `posthorn warmup start <mailbox-id>`
|
|
55
101
|
|
|
56
102
|
Dependencies: you CANNOT create a mailbox until the domain status is `ready`.
|
|
57
|
-
You CANNOT send or start warmup until the account is "verified" tier.
|
|
58
103
|
|
|
59
104
|
Run `posthorn auth status --json` any time to see where things stand. State is
|
|
60
105
|
stored locally, so you can stop and continue any time.
|
|
@@ -66,11 +111,13 @@ click-by-click instructions. Before each browser step, share with the user that
|
|
|
66
111
|
this is a one-time setup, e.g. "This is a one-time setup step; you won't need
|
|
67
112
|
to do this again." The repetition model, so you can answer follow-ups:
|
|
68
113
|
- Cloudflare API token: once ever
|
|
114
|
+
- Workspace signup (only when creating a new org): once ever
|
|
69
115
|
- Workspace delegation: once per Workspace org
|
|
70
116
|
- DKIM: once per domain
|
|
71
117
|
- Everything else (domains, mailboxes, warmup): fully automated, no browser
|
|
72
118
|
|
|
73
119
|
### Cloudflare API token (one-time, for "buy new" or "own Cloudflare" domains)
|
|
120
|
+
- Tell the user: "This is a one-time setup; you won't need to do this again."
|
|
74
121
|
- dash.cloudflare.com/profile/api-tokens → Create Token → Custom Token
|
|
75
122
|
- Permissions: Account>Account Settings>Read, Account>Registrar: Domains>Admin,
|
|
76
123
|
Zone>Zone>Edit, Zone>DNS>Edit.
|
|
@@ -87,12 +134,16 @@ to do this again." The repetition model, so you can answer follow-ups:
|
|
|
87
134
|
account (they pay Cloudflare directly; Posthorn never bills for domains).
|
|
88
135
|
|
|
89
136
|
### Google Workspace domain-wide delegation (one-time per Workspace org)
|
|
137
|
+
- Tell the user: "This is a one-time setup for your Workspace; you won't need
|
|
138
|
+
to do this again."
|
|
90
139
|
- admin.google.com → Security → Access and data control → API controls →
|
|
91
140
|
Domain-wide delegation → Add new
|
|
92
141
|
- Client ID: 110137377718772968374
|
|
93
142
|
- OAuth scopes (paste all, comma-separated):
|
|
94
143
|
`https://mail.google.com/,https://www.googleapis.com/auth/admin.directory.user,https://www.googleapis.com/auth/admin.directory.domain,https://www.googleapis.com/auth/siteverification`
|
|
95
|
-
-
|
|
144
|
+
- Must be done signed in as a SUPER-ADMIN of that Workspace org, and the email
|
|
145
|
+
passed to the CLI must be that super-admin's. Then run:
|
|
146
|
+
`posthorn accounts workspace <admin-email>`
|
|
96
147
|
- This replaces OAuth entirely: no consent screen, no app verification, no test users.
|
|
97
148
|
|
|
98
149
|
### Nameservers (only for "managed DNS" or moving a domain to Cloudflare)
|
|
@@ -102,6 +153,8 @@ to do this again." The repetition model, so you can answer follow-ups:
|
|
|
102
153
|
(managed) or by polling `posthorn domains get <id>`.
|
|
103
154
|
|
|
104
155
|
### DKIM (per domain, Google has no DKIM API)
|
|
156
|
+
Tell the user: "This is a once-per-domain step; new mailboxes on this domain
|
|
157
|
+
won't need it."
|
|
105
158
|
If you have browser-automation tools, do it for the user:
|
|
106
159
|
- Navigate to https://admin.google.com/ac/apps/gmail/authenticateemail
|
|
107
160
|
(the URL may need /u/N/ for the right account slot, verify the "Selected
|
|
@@ -114,37 +167,6 @@ If you have browser-automation tools, do it for the user:
|
|
|
114
167
|
If no browser tools: have the user do the above and paste you the TXT value,
|
|
115
168
|
then add it to their DNS.
|
|
116
169
|
|
|
117
|
-
## Domain options (3 paths)
|
|
118
|
-
|
|
119
|
-
PRINCIPLE: domain purchase ALWAYS happens on the user's own Cloudflare account
|
|
120
|
-
and card. Posthorn never owns or bills for domains.
|
|
121
|
-
|
|
122
|
-
1. NEW domain → requires the user's own Cloudflare account (+ payment method).
|
|
123
|
-
- `posthorn domains check name1.com name2.io --cloudflare <id>`, fast availability + price
|
|
124
|
-
- `posthorn domains reputation <domain>`, VET it before buying (Spamhaus
|
|
125
|
-
blocklist/score + archive.org prior-use). Verdicts: clean / caution (used
|
|
126
|
-
before) / avoid (blocklisted or poor reputation). Run this on the candidate
|
|
127
|
-
before `buy`; you can't tell a tainted domain from its name. (Slower + uses
|
|
128
|
-
a quota, so it's a separate explicit call, not part of `check`.)
|
|
129
|
-
- `posthorn domains buy <domain> --cloudflare <id> --contact '{...}'`
|
|
130
|
-
|
|
131
|
-
2. EXISTING domain, recommended → user's own Cloudflare account.
|
|
132
|
-
- `posthorn domains connect <domain> --cloudflare <account-id>`
|
|
133
|
-
- (If the domain isn't on Cloudflare yet, the user adds it at dash.cloudflare.com
|
|
134
|
-
and points nameservers to their account first.)
|
|
135
|
-
|
|
136
|
-
3. EXISTING domain, optional → Posthorn-managed DNS (no Cloudflare account needed).
|
|
137
|
-
- `posthorn domains managed <domain>` → prints 2 nameservers
|
|
138
|
-
- user sets them at their registrar
|
|
139
|
-
- `posthorn domains activate <domain-id>` → checks propagation, starts DNS setup
|
|
140
|
-
- Best for DEDICATED email-only domains. Don't use on a domain running a live
|
|
141
|
-
website; Posthorn becomes authoritative for ALL its DNS.
|
|
142
|
-
|
|
143
|
-
After any path, poll `posthorn domains get <id>` until status is `ready`. DNS
|
|
144
|
-
records (MX, SPF, DMARC) are configured automatically. Statuses progress:
|
|
145
|
-
pending_* → purchased → dns_configuring → dns_configured → dns_verified →
|
|
146
|
-
workspace_verifying → workspace_verified → ready.
|
|
147
|
-
|
|
148
170
|
## Mailboxes & sending
|
|
149
171
|
|
|
150
172
|
- Create: `posthorn mailboxes create <domain-id> --email john@dom.com --first John --last Smith`
|
|
@@ -152,19 +174,17 @@ workspace_verifying → workspace_verified → ready.
|
|
|
152
174
|
- Credentials for IMAP/SMTP are auto-provisioned via the service account
|
|
153
175
|
(domain-wide delegation). Google mailboxes need no per-mailbox setup.
|
|
154
176
|
- Send: `posthorn send <mailbox-id> --to x@y.com --subject "Hi" --body "..."`
|
|
155
|
-
(requires "verified" tier)
|
|
156
177
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Anonymous tier can do setup (accounts, domains). To send/warmup, verify:
|
|
160
|
-
`posthorn auth verify` → choose admin email or a custom email → confirm the code.
|
|
178
|
+
There is no email-verification step anywhere: connecting your own Cloudflare
|
|
179
|
+
and Workspace IS the identity. Once a mailbox exists, sending and warmup work.
|
|
161
180
|
|
|
162
181
|
## Warmup
|
|
163
182
|
|
|
164
|
-
- `posthorn warmup start <mailbox-id>` → joins the shared warmup pool
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
- `posthorn warmup start <mailbox-id>` → joins the shared warmup pool and runs
|
|
184
|
+
continuously until paused
|
|
185
|
+
- Volume and reply behavior are platform-managed (ramps from 2/day up to
|
|
186
|
+
5/day, then holds; peers reply to ~30% of warmup mail). Not configurable:
|
|
187
|
+
the pool is shared, so settings are tuned for everyone's deliverability.
|
|
168
188
|
- `posthorn warmup stats <campaign-id>` → placement rate, reputation, daily breakdown
|
|
169
189
|
- `posthorn warmup list` → all campaigns
|
|
170
190
|
- Warmup never fully stops on its own, pause with `posthorn warmup pause <id>`.
|
|
@@ -174,19 +194,17 @@ Anonymous tier can do setup (accounts, domains). To send/warmup, verify:
|
|
|
174
194
|
`posthorn feedback --type bug|feature|question|other "<message>"`
|
|
175
195
|
|
|
176
196
|
If you hit a bug, missing capability, or rough edge while driving this CLI,
|
|
177
|
-
report it
|
|
197
|
+
report it; it goes straight to the Posthorn team. Agents: you are encouraged
|
|
178
198
|
to file feedback yourself when something doesn't work the way you expected
|
|
179
|
-
(include the command you ran and what you expected).
|
|
180
|
-
|
|
199
|
+
(include the command you ran and what you expected).
|
|
200
|
+
|
|
201
|
+
Whenever you or the user get stuck, file the issue with `posthorn feedback`.
|
|
181
202
|
|
|
182
203
|
## Tips for agents
|
|
183
204
|
|
|
184
205
|
- Read commands auto-detect output: when stdout isn't a TTY (i.e. you're an agent
|
|
185
206
|
capturing output), they emit JSON automatically, no flag needed. Humans in a
|
|
186
207
|
terminal get formatted text. Force either way with `--json` / `--pretty`.
|
|
187
|
-
- Async steps (domain provisioning, nameserver propagation) need polling, not blocking.
|
|
188
208
|
- Every command stores state locally (~/.config), so the user doesn't re-enter keys.
|
|
189
209
|
- Translate everything into plain English for the user. Never show them raw JSON
|
|
190
210
|
or ask them to run curl, you run the CLI on their behalf.
|
|
191
|
-
- When a step needs the user's browser (Cloudflare token, delegation, nameservers,
|
|
192
|
-
DKIM), give exact click-by-click instructions and wait for confirmation.
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,6 @@ async function register(options) {
|
|
|
76
76
|
spinner.succeed("Account created!");
|
|
77
77
|
console.log();
|
|
78
78
|
console.log(` API Key: ${chalk.cyan(data.api_key)}`);
|
|
79
|
-
console.log(` Tier: ${chalk.yellow(data.user.tier)}`);
|
|
80
79
|
console.log();
|
|
81
80
|
console.log(chalk.dim(" Save this API key, it won't be shown again."));
|
|
82
81
|
console.log(chalk.dim(` Config stored at: ${getConfigPath()}`));
|
|
@@ -581,7 +580,7 @@ function guide() {
|
|
|
581
580
|
|
|
582
581
|
// src/index.ts
|
|
583
582
|
var program = new Command();
|
|
584
|
-
program.name("posthorn").description("Posthorn: domain setup, mailbox creation, and email warmup").version("0.2.
|
|
583
|
+
program.name("posthorn").description("Posthorn: domain setup, mailbox creation, and email warmup").version("0.2.9");
|
|
585
584
|
program.addHelpText("after", `
|
|
586
585
|
Agents: run 'posthorn guide' first for the full workflow playbook.
|
|
587
586
|
|
|
@@ -593,7 +592,6 @@ Typical flow:
|
|
|
593
592
|
posthorn domains connect <domain> --cloudflare <id>
|
|
594
593
|
posthorn domains get <id> --json poll until status: ready
|
|
595
594
|
posthorn mailboxes create <domain-id> --email you@dom.com --first A --last B
|
|
596
|
-
posthorn auth verify unlock sending + warmup
|
|
597
595
|
posthorn warmup start <mailbox-id>
|
|
598
596
|
|
|
599
597
|
Output: read commands auto-detect. Agents (non-TTY) get JSON, humans get
|
|
@@ -634,7 +632,9 @@ program.parseAsync(process.argv).catch((err) => {
|
|
|
634
632
|
if (err.statusCode === 401) {
|
|
635
633
|
console.log(chalk7.red("\n Authentication failed. Run: posthorn auth register\n"));
|
|
636
634
|
} else if (err.statusCode === 403) {
|
|
637
|
-
console.log(chalk7.red(
|
|
635
|
+
console.log(chalk7.red(`
|
|
636
|
+
Not allowed: ${err.message}
|
|
637
|
+
`));
|
|
638
638
|
} else if (err.statusCode === 429) {
|
|
639
639
|
console.log(chalk7.yellow("\n Rate limit exceeded. Try again in a minute.\n"));
|
|
640
640
|
} else {
|