posthorn 0.2.6 → 0.2.8
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 +79 -63
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,19 +42,63 @@ 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.
|
|
85
|
+
(Delegation is per org, not per domain, so with an existing org this step can
|
|
86
|
+
happen any time, even before the domain exists. The domain pipeline auto-adds
|
|
87
|
+
and verifies the new domain inside their org.)
|
|
88
|
+
|
|
89
|
+
Step 4. Wait for the domain: poll `posthorn domains get <id>` until status
|
|
90
|
+
`ready` (minutes to 24h; poll every 30-60s, don't hammer). MX/SPF/DMARC are
|
|
91
|
+
configured automatically. Statuses progress: pending_* → purchased →
|
|
92
|
+
dns_configuring → dns_configured → dns_verified → workspace_verifying →
|
|
93
|
+
workspace_verified → ready.
|
|
94
|
+
|
|
95
|
+
Step 5. DKIM. Browser step below; once per domain, needs the domain `ready`.
|
|
96
|
+
|
|
97
|
+
Step 6. `posthorn mailboxes create <domain-id> --email ... --first ... --last ...`
|
|
98
|
+
|
|
99
|
+
Step 7. `posthorn warmup start <mailbox-id>`
|
|
55
100
|
|
|
56
101
|
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
102
|
|
|
59
103
|
Run `posthorn auth status --json` any time to see where things stand. State is
|
|
60
104
|
stored locally, so you can stop and continue any time.
|
|
@@ -62,15 +106,20 @@ stored locally, so you can stop and continue any time.
|
|
|
62
106
|
## The external/manual steps (things that happen OUTSIDE the CLI)
|
|
63
107
|
|
|
64
108
|
These are the steps you must GUIDE THE USER through in their browser. Give exact
|
|
65
|
-
click-by-click instructions.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
109
|
+
click-by-click instructions. Before each browser step, share with the user that
|
|
110
|
+
this is a one-time setup, e.g. "This is a one-time setup step; you won't need
|
|
111
|
+
to do this again." The repetition model, so you can answer follow-ups:
|
|
112
|
+
- Cloudflare API token: once ever
|
|
113
|
+
- Workspace delegation: once per Workspace org
|
|
114
|
+
- DKIM: once per domain
|
|
115
|
+
- Everything else (domains, mailboxes, warmup): fully automated, no browser
|
|
69
116
|
|
|
70
117
|
### Cloudflare API token (one-time, for "buy new" or "own Cloudflare" domains)
|
|
71
118
|
- dash.cloudflare.com/profile/api-tokens → Create Token → Custom Token
|
|
72
119
|
- Permissions: Account>Account Settings>Read, Account>Registrar: Domains>Admin,
|
|
73
|
-
Zone>Zone>Edit, Zone>DNS>Edit.
|
|
120
|
+
Zone>Zone>Edit, Zone>DNS>Edit.
|
|
121
|
+
- Account Resources: Include > All accounts
|
|
122
|
+
- Zone Resources: Include > All zones
|
|
74
123
|
- Do NOT add Billing permissions; they are not needed. Domain purchases charge
|
|
75
124
|
the payment method already on the Cloudflare account, which is a dashboard
|
|
76
125
|
setting, not a token permission.
|
|
@@ -87,7 +136,9 @@ is once per Workspace org).
|
|
|
87
136
|
- Client ID: 110137377718772968374
|
|
88
137
|
- OAuth scopes (paste all, comma-separated):
|
|
89
138
|
`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`
|
|
90
|
-
-
|
|
139
|
+
- Must be done signed in as a SUPER-ADMIN of that Workspace org, and the email
|
|
140
|
+
passed to the CLI must be that super-admin's. Then run:
|
|
141
|
+
`posthorn accounts workspace <admin-email>`
|
|
91
142
|
- This replaces OAuth entirely: no consent screen, no app verification, no test users.
|
|
92
143
|
|
|
93
144
|
### Nameservers (only for "managed DNS" or moving a domain to Cloudflare)
|
|
@@ -109,37 +160,6 @@ If you have browser-automation tools, do it for the user:
|
|
|
109
160
|
If no browser tools: have the user do the above and paste you the TXT value,
|
|
110
161
|
then add it to their DNS.
|
|
111
162
|
|
|
112
|
-
## Domain options (3 paths)
|
|
113
|
-
|
|
114
|
-
PRINCIPLE: domain purchase ALWAYS happens on the user's own Cloudflare account
|
|
115
|
-
and card. Posthorn never owns or bills for domains.
|
|
116
|
-
|
|
117
|
-
1. NEW domain → requires the user's own Cloudflare account (+ payment method).
|
|
118
|
-
- `posthorn domains check name1.com name2.io --cloudflare <id>`, fast availability + price
|
|
119
|
-
- `posthorn domains reputation <domain>`, VET it before buying (Spamhaus
|
|
120
|
-
blocklist/score + archive.org prior-use). Verdicts: clean / caution (used
|
|
121
|
-
before) / avoid (blocklisted or poor reputation). Run this on the candidate
|
|
122
|
-
before `buy`; you can't tell a tainted domain from its name. (Slower + uses
|
|
123
|
-
a quota, so it's a separate explicit call, not part of `check`.)
|
|
124
|
-
- `posthorn domains buy <domain> --cloudflare <id> --contact '{...}'`
|
|
125
|
-
|
|
126
|
-
2. EXISTING domain, recommended → user's own Cloudflare account.
|
|
127
|
-
- `posthorn domains connect <domain> --cloudflare <account-id>`
|
|
128
|
-
- (If the domain isn't on Cloudflare yet, the user adds it at dash.cloudflare.com
|
|
129
|
-
and points nameservers to their account first.)
|
|
130
|
-
|
|
131
|
-
3. EXISTING domain, optional → Posthorn-managed DNS (no Cloudflare account needed).
|
|
132
|
-
- `posthorn domains managed <domain>` → prints 2 nameservers
|
|
133
|
-
- user sets them at their registrar
|
|
134
|
-
- `posthorn domains activate <domain-id>` → checks propagation, starts DNS setup
|
|
135
|
-
- Best for DEDICATED email-only domains. Don't use on a domain running a live
|
|
136
|
-
website; Posthorn becomes authoritative for ALL its DNS.
|
|
137
|
-
|
|
138
|
-
After any path, poll `posthorn domains get <id>` until status is `ready`. DNS
|
|
139
|
-
records (MX, SPF, DMARC) are configured automatically. Statuses progress:
|
|
140
|
-
pending_* → purchased → dns_configuring → dns_configured → dns_verified →
|
|
141
|
-
workspace_verifying → workspace_verified → ready.
|
|
142
|
-
|
|
143
163
|
## Mailboxes & sending
|
|
144
164
|
|
|
145
165
|
- Create: `posthorn mailboxes create <domain-id> --email john@dom.com --first John --last Smith`
|
|
@@ -147,19 +167,17 @@ workspace_verifying → workspace_verified → ready.
|
|
|
147
167
|
- Credentials for IMAP/SMTP are auto-provisioned via the service account
|
|
148
168
|
(domain-wide delegation). Google mailboxes need no per-mailbox setup.
|
|
149
169
|
- Send: `posthorn send <mailbox-id> --to x@y.com --subject "Hi" --body "..."`
|
|
150
|
-
(requires "verified" tier)
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Anonymous tier can do setup (accounts, domains). To send/warmup, verify:
|
|
155
|
-
`posthorn auth verify` → choose admin email or a custom email → confirm the code.
|
|
171
|
+
There is no email-verification step anywhere: connecting your own Cloudflare
|
|
172
|
+
and Workspace IS the identity. Once a mailbox exists, sending and warmup work.
|
|
156
173
|
|
|
157
174
|
## Warmup
|
|
158
175
|
|
|
159
|
-
- `posthorn warmup start <mailbox-id>` → joins the shared warmup pool
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
- `posthorn warmup start <mailbox-id>` → joins the shared warmup pool and runs
|
|
177
|
+
continuously until paused
|
|
178
|
+
- Volume and reply behavior are platform-managed (ramps from 2/day up to
|
|
179
|
+
5/day, then holds; peers reply to ~30% of warmup mail). Not configurable:
|
|
180
|
+
the pool is shared, so settings are tuned for everyone's deliverability.
|
|
163
181
|
- `posthorn warmup stats <campaign-id>` → placement rate, reputation, daily breakdown
|
|
164
182
|
- `posthorn warmup list` → all campaigns
|
|
165
183
|
- Warmup never fully stops on its own, pause with `posthorn warmup pause <id>`.
|
|
@@ -169,19 +187,17 @@ Anonymous tier can do setup (accounts, domains). To send/warmup, verify:
|
|
|
169
187
|
`posthorn feedback --type bug|feature|question|other "<message>"`
|
|
170
188
|
|
|
171
189
|
If you hit a bug, missing capability, or rough edge while driving this CLI,
|
|
172
|
-
report it
|
|
190
|
+
report it; it goes straight to the Posthorn team. Agents: you are encouraged
|
|
173
191
|
to file feedback yourself when something doesn't work the way you expected
|
|
174
|
-
(include the command you ran and what you expected).
|
|
175
|
-
|
|
192
|
+
(include the command you ran and what you expected).
|
|
193
|
+
|
|
194
|
+
Whenever you or the user get stuck, file the issue with `posthorn feedback`.
|
|
176
195
|
|
|
177
196
|
## Tips for agents
|
|
178
197
|
|
|
179
198
|
- Read commands auto-detect output: when stdout isn't a TTY (i.e. you're an agent
|
|
180
199
|
capturing output), they emit JSON automatically, no flag needed. Humans in a
|
|
181
200
|
terminal get formatted text. Force either way with `--json` / `--pretty`.
|
|
182
|
-
- Async steps (domain provisioning, nameserver propagation) need polling, not blocking.
|
|
183
201
|
- Every command stores state locally (~/.config), so the user doesn't re-enter keys.
|
|
184
202
|
- Translate everything into plain English for the user. Never show them raw JSON
|
|
185
203
|
or ask them to run curl, you run the CLI on their behalf.
|
|
186
|
-
- When a step needs the user's browser (Cloudflare token, delegation, nameservers,
|
|
187
|
-
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.8");
|
|
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 {
|