
By the end of this guide, you can have a working first version of a phone-driven publishing system in about three focused hours. You will be able to start with a conversation, shape the argument, create a file-backed draft, review the real page on your phone, and publish only when you explicitly approve it.
You don't need my exact stack to copy the idea. You need a file-backed blog, a conversational workspace with durable instructions, a Git repository, and a protected preview before production.
I want to build a phone-first publishing system and blog from scratch. The core loop should be: chat, question, argue, compose, preview, and publish. Use Astro with Markdown or MDX, keep GitHub as the source of truth, deploy through protected Vercel previews, and help me connect a custom domain. Keep saving separate from publishing. Start with a simple Version 1 that proves the loop, then add a Version 2 with exact-preview approval, path allowlists, validation, and deployment verification. Ask me for my publication name, author name, visual direction, domain status, account status, and technical comfort level before making changes. Keep new posts private by default, and don't buy anything, change DNS, commit, push, merge, or publish without explaining the step and getting the permission required at that moment. Then take me through the build one verified phase at a time.I recently wrote about wanting a blog I could talk to.
Not a machine that turns one prompt into a finished article. A place where I could begin with a thought that was still unclear, talk through it, question it, argue with it, and eventually discover what I was actually trying to say.
The important part was what happened next.
I didn't want the conversation trapped in a chat window. I wanted it to become a real draft on my website. I wanted to open the finished page on my phone, read it in its actual layout, make changes through the same conversation, and publish only when I was ready.
The workflow I ended up with looks like this:
The phone is the main interface. GitHub holds the files. Vercel builds the previews and public site. Astro renders the blog. Tina gives me a visual editor when I am working locally.
There is no database of posts. I don't use Tina Cloud. My writing lives in ordinary files, in a repository I control.
It took two versions to get there.
Version one made the loop possible.
Version two made it trustworthy.
The first version had one job: carry an idea from a phone conversation to a real webpage.
Every post on my site is an MDX file.
MDX is close to ordinary Markdown. The file contains some metadata at the top, followed by the article itself:
---
title: A specific title
description: A concise description.
pubDate: 2026-09-15T09:00:00.000Z
draft: true
tags:
- AI
- Writing
author: Your Name
---
The article starts here.The filename becomes the URL. A file named:
src/content/posts/the-useful-distance.mdxbecomes:
/blog/the-useful-distance/That file is the source of truth. I can edit it in a text editor, through an AI coding agent, or in a visual editor. Every interface is working on the same underlying document.
Astro reads the MDX files and turns them into a static website. The production site doesn't need to query a CMS every time somebody opens an article. It is a collection of generated pages that can be deployed almost anywhere.
That keeps the foundation simple.
I use TinaCMS for local editing, but I don't use Tina Cloud.
When I run:
bun run authorthe command starts the Astro site and Tina's local editing server. I can open the blog at:
http://localhost:4321and the editor at:
http://localhost:4321/admin/Saving in the editor writes directly to the MDX file in my repository. Tina's documentation describes this as local mode: the local content API reads from and writes to the filesystem, and the cloud token and client ID are not used. That local workflow is documented here.
In my setup, the editor is a local tool. It isn't part of the deployed production site.
The public site builds from the same files through Astro's content layer. That matters because a clean production build should not depend on a Tina server running on my computer.
It also means Tina is optional. If I stopped using it tomorrow, the articles would still be MDX files. The website would still work. I would only lose one editing interface.
The next step was giving the conversation enough context to be useful.
I created a ChatGPT Project for the blog. Projects let me keep related chats, files, and instructions together, so a new conversation doesn't have to begin from zero.
The Project contains a small set of files:
The voice file is not a list of magic adjectives. It is a working hypothesis.
It says things like: preserve the original degree of certainty, prefer concrete observations, remove generic AI phrasing, and undo an edit if it makes the prose smoother but less recognizably mine.
That gives the conversation a useful starting point without asking it to imitate a caricature of me.
I can then begin with ordinary language:
I want to think through a post about something I noticed.
From there, the conversation can stay exploratory.
What is the real question? Is my explanation too convenient? What would make the argument false? Am I describing one experience as if it were universal? Is there a better example? Is this observation interesting enough to carry an essay?
I don't ask for an outline immediately. I let the idea develop until there is something worth structuring.
When an article begins to emerge, the conversation maintains one canonical draft. We revise that draft instead of producing six polished alternatives and losing track of which one matters.
A good draft inside ChatGPT is still only a draft inside ChatGPT.
The next step is the bridge to the repository.
The GitHub connection lets ChatGPT retrieve authorized repository content on demand. Availability and write capabilities can vary by account, workspace, and product surface, so that is something to confirm before depending on it. OpenAI documents the current GitHub behavior here.
In my phone workflow, the conversation can create or update the post in the repository. New posts always begin with:
draft: trueThe work goes onto a branch named after the article:
draft/the-useful-distanceThe branch contains only the post and its images. It opens or updates a draft pull request into main.
At this point, the post has left the chat. It is now a versioned file with an inspectable diff.
This is where the workflow started to feel different.
I can say:
Preview this on my phone.
The system saves the current draft, pushes the branch, and waits for Vercel to build it.
Vercel creates a unique Preview deployment for a branch or pull request. Its deployment model separates Local, Preview, and Production environments, which maps neatly onto the writing process.
The conversation gives me a link to the actual article.
I open it on my phone and see the real page: typography, spacing, images, pull quotes, diagrams, links, dark mode, and responsive layout. I am no longer reviewing Markdown or prose pasted into a chat. I am reviewing the thing a reader would see.
The preview has a visible banner reminding me that it is a draft. It also blocks search indexing.
But noindex is not privacy.
Preview builds intentionally contain drafts so the homepage, tags, navigation, and links can be tested together. I therefore use Vercel Authentication to protect them. Vercel supports restricting access to non-public deployments through its Deployment Protection settings.
That protected page becomes the object I review.
I can return to the conversation and say:
The introduction takes too long. Cut the first three paragraphs and make the comparison more concrete.
The draft changes. A new commit produces a new preview. I read that version instead.
When the exact preview is ready, I return to the chat and say:
Publish this preview.
That phrase is intentionally specific.
"Save it" doesn't publish.
"Preview it" doesn't publish.
"Looks good" doesn't publish.
Publishing changes the post from:
draft: trueto:
draft: falseThe approved branch is merged into main, Vercel creates a Production deployment, and the conversation waits for that deployment to succeed before telling me the article is live.
That was version one.
I could begin with an uncertain thought on my phone and stay in the same conversation through questioning, composition, revision, preview, and publication.
It worked.
Then I started thinking about all the ways it could go wrong.
The second version was less about adding capabilities and more about defining boundaries.
If an AI system can write to the repository that publishes your website, "be careful" is not a sufficient safety model.
The rules need to exist in the system itself.
I now think of an article as moving through a sequence:
exploring
→ brief
→ drafting
→ revising
→ designed
→ validated
→ publishedThese are editorial states, not merely technical ones.
During exploration, the goal is to find the thought.
During drafting, the goal is to make it readable.
During revision, the goal is to make it accurate and recognizably mine.
The design pass comes after the prose is stable. Validation checks the actual file. Publication is a separate, explicit action.
This prevents the system from racing toward an artifact before the thinking is ready.
A preview link is not just a convenience. It is the approval boundary.
Every Git commit has a unique identifier. When Vercel builds the draft, the preview is tied to that exact commit.
Publishing is allowed only if:
If the article changes after I review it, the old approval no longer applies.
The system must create a new preview.
This sounds strict until you imagine the alternative: approving one page while a slightly different version is what actually goes live.
For a post named the-useful-distance, the publishing workflow may touch only:
src/content/posts/the-useful-distance.mdx
public/images/the-useful-distance/If the branch contains a workflow change, a configuration file, another article, or an unrelated image, publication stops.
This is one of the most important guardrails in the system.
The permission is not "change the repository." The permission is "work on this article."
A successful merge doesn't prove that a page is live.
The build might fail. The deployment might be delayed. The domain might still point at an older version.
The workflow therefore waits for a successful Vercel Production deployment and returns the public article URL.
Until then, the post is not reported as live.
This seems like a small distinction, but it changes the meaning of completion. The system reports the outcome I care about, not merely the last technical action it attempted.
The ChatGPT Project accumulates context as we work, but the durable version belongs in GitHub.
The repository is the canonical home for:
The Project contains working copies of those files. Individual chats contain the exploratory material around a particular article.
I don't move every conversation back into the repository. Most of it is scaffolding.
But if a conversation reveals a durable preference, I update the versioned context. For example: a recurring phrase I dislike, a better rule for handling uncertain claims, or a change to the publication workflow.
The direction is deliberate:
Conversation reveals a lesson
→ propose a context change
→ review it
→ commit it to GitHub
→ update the Project copyBefore saving or publishing, the system reads the current operational instructions from the repository. That protects against an older uploaded Project file silently governing a newer publishing workflow.
The writing room can learn, but its memory is reviewed.
MDX lets an article contain reusable components alongside Markdown.
My component set includes:
The components are not there to make every article look elaborate.
They are there because some ideas are easier to understand as a contrast, sequence, number, or picture.
The editorial rule is simple: stabilize the prose first, then ask whether a component makes the argument clearer.
A typical article uses zero to four.
If the component is doing no explanatory work, it doesn't belong.
The minimum setup is smaller than the complete system:
Tina is optional. It gives you a good local visual editor, but the writing should remain usable without it.
The guarded publisher, local approval tokens, custom agent skills, and MCP interface are also optional. They make the system safer and more flexible, but they are version two work.
Start by proving the loop.
Assume you are beginning with an empty folder and a domain name you may not have purchased yet. The goal is to finish with four connected things:
The prompt below is designed to build that system with you. Paste it into an LLM that can create files and run commands, such as a Codex task or another coding agent. If your LLM can only chat, use the same prompt but expect to copy its files and commands into a local terminal yourself.
Help me build a phone-first blog publishing system from scratch. I am starting without a website or repository. The finished workflow should let me use a conversation on my phone to question an idea, argue with it, compose an article, save it as a private draft, review the real rendered page, request revisions, and publish only the exact version I approve.
Use this default stack unless you find a concrete reason to recommend a change: Astro, MDX files, TypeScript, Bun, GitHub, and Vercel. GitHub must be the canonical source of truth. Posts must remain ordinary files. TinaCMS may be added later as a local filesystem editor, but don't use Tina Cloud or make production depend on Tina.
Work with me in phases. At the beginning, ask only for the information you actually need: the blog name, author name, visual direction, whether I already own a domain, the domain or registrar if known, whether my GitHub and Vercel accounts exist, and whether you can edit local files and use GitHub from this environment. If I haven't chosen a domain or visual direction, give me a sensible placeholder and keep going.
Phase 1 — Scaffold the site. Create a new Astro project with MDX and TypeScript. Add a home page, an essays index, an individual post route, RSS, a 404 page, basic responsive typography, light and dark themes, and metadata for search and social sharing. Store posts in src/content/posts and images in public/images/<slug>. Create a typed content schema with title, description, publication date, draft status, tags, author, optional hero image, and hero-image alt text. Add one sample post. New posts must default to draft: true. Run the site locally and show me the local URL.
Phase 2 — Make drafts safe. Exclude drafts from the production home page, archive, tags, RSS, sitemap, and production routes. Allow drafts in local development and intentional preview builds. Put a clear LOCAL DRAFT or PREVIEW DRAFT banner on draft pages, add noindex metadata, and explain that noindex isn't privacy. Add validation for required metadata, duplicate slugs, missing images, and missing alt text. Add commands for development, checking, production builds, preview builds, and post validation. Run every check and fix failures before moving on.
Phase 3 — Put it in GitHub. Initialize Git, add an appropriate .gitignore and README, and show me the proposed initial commit. Ask before creating a remote repository, committing, or pushing. Once I approve, create or connect the GitHub repository and push the initial branch. Explain each account or permission step I must complete myself.
Phase 4 — Deploy it. Connect the repository to Vercel. Configure the normal production build so draft posts stay excluded. Configure branch or pull-request previews so the selected draft can be rendered. Protect previews with Vercel Authentication or the strongest access control available on my plan; if private preview protection isn't available, stop and explain the safer alternatives. Verify the production URL and one draft preview URL.
Phase 5 — Add my custom domain. If I don't own a domain, help me choose and purchase one, but let me perform the purchase. Add the domain to the Vercel project, show me the exact DNS records Vercel requests, and tell me where to enter them at my registrar. Wait for me to confirm the DNS change. Then verify the domain, HTTPS, the preferred www or apex version, and the redirect from the other version. Update the Astro site URL, canonical URLs, sitemap, RSS, and social metadata to use the custom domain.
Phase 6 — Build the writing room. Create a versioned context directory in the repository containing PROJECT_INSTRUCTIONS.md, VOICE.md, EDITORIAL_CHARTER.md, BLOG_SCHEMA.md, STORY_COMPONENTS.md, and PUBLISHING_POLICY.md. Ask me short questions to draft the voice and editorial files. The publishing policy must distinguish explore, save, preview, revise, approve, and publish. Keep working copies in my LLM Project if supported, but treat the GitHub versions as canonical. Don't copy every chat into GitHub; propose updates only when a conversation reveals a durable rule.
Phase 7 — Implement the phone workflow. For each article, use one branch named draft/<slug>, one MDX file at src/content/posts/<slug>.mdx, and one image directory at public/images/<slug>/. Let me begin with an informal conversation, challenge the idea before outlining it, maintain one canonical draft, and create the file only when I ask. Open or update a draft pull request, wait for its protected preview, and return the direct article URL for phone review. When I request changes, update that same branch and return the new preview.
Phase 8 — Guard publication. Saving, previewing, revising, approving a sentence, or saying 'looks good' must not publish. Accept publication only after I explicitly say 'Publish this preview' or equally specific words. Record the reviewed commit SHA and successful preview deployment. Before publishing, confirm that the pull request still points to that SHA, the preview succeeded, validation and the production build pass, and the branch changes only the selected post and its image directory. If the content changes, invalidate the approval and require a new preview. After explicit approval, change draft to false, merge the approved work, wait for a successful production deployment, and verify the public article URL before reporting that it is live.
Phase 9 — Rehearse it. Create a disposable draft and walk me through the full phone loop: chat, question, argue, compose, preview, revise, preview again, and publish. Confirm that the first preview loses approval after the revision. Test the live URL, mobile layout, dark mode, RSS, metadata, and custom-domain redirects. Leave me with a concise operating guide containing the exact phrases and commands I will use for future posts.
Safety rules: Show me the plan and files for each phase before making broad changes. Work incrementally and verify each checkpoint. Don't buy a domain, change DNS, create external accounts, expose a draft, commit, push, open or merge a pull request, or publish without explaining the action and getting the permission required at that moment. Never place secrets in the repository. If a capability isn't available in this environment, explain the manual step clearly and continue with everything else that can be completed safely. Start with Phase 0: ask the short setup questions, then propose the project structure and implementation plan.That prompt gives the agent the whole destination, but you should still build it in stages. The checkpoints below tell you what to expect and how to know each stage works.
Create GitHub and Vercel accounts if you don't already have them. Install Git, a current Node.js release, and Bun on your computer. You can wait to buy the domain, but decide which registrar you will use.
Give the coding agent an empty folder. Tell it the publication name, author name, and a rough visual direction. It should respond with a proposed architecture before it starts making files.
Checkpoint: git --version, node --version, and bun --version all return a version number. You can sign in to GitHub and Vercel.
The agent should create an Astro project, add MDX support, and establish a predictable structure:
src/content/posts/ Article files
src/pages/ Home, archive, article, RSS, and 404 routes
src/layouts/ Shared page and article layouts
src/components/ Navigation and article components
src/styles/ Global typography and themes
public/images/<slug>/ Images belonging to one articleAsk for one sample post so you can see the complete path from file to page. The agent should start the development server and give you a local URL.
Checkpoint: the home page, essays index, and sample article load locally. The layout works at a phone-sized width and in light and dark mode.
Every post should use the same metadata. At minimum, require a title, description, publication date, draft status, tags, author, and accessible text for any hero image.
The important default is:
draft: trueAsk the agent to make that default difficult to miss. It should also validate dates, duplicate URLs, missing images, and missing alt text before a draft can move forward.
Checkpoint: an invalid sample post fails validation with a useful message. A valid draft renders locally but doesn't appear in a production build.
You need three distinct environments:
Local Write and inspect drafts on your computer
Preview Review a specific branch through a protected URL
Production Show only published work at your public domainAsk the agent to test draft filtering everywhere, not only on the home page. Drafts should stay out of archives, tag pages, RSS, sitemaps, and production article routes. Preview pages should carry a visible draft banner and noindex, but those measures don't make the writing private.
Checkpoint: the draft is visible locally and in the intended preview build, but its production URL returns a 404 and it never appears in public feeds or lists.
Review the generated files before the first commit. Confirm that secrets, local environment files, build output, and dependency directories are ignored.
Create a GitHub repository, connect it as the remote, and push the initial branch. If your agent can perform GitHub actions, let it prepare these steps but keep the approval boundary clear. If it can't, it should give you the exact commands to run.
Checkpoint: the GitHub repository contains the source files, README, and versioned writing-room context. It contains no tokens, passwords, or generated build directories.
Import the GitHub repository into Vercel. Accept the detected Astro settings unless the agent has a specific reason to change them. Confirm the build command and output directory, then deploy.
At this point, the temporary Vercel URL is useful. Open it on your phone and check the navigation, type size, line length, images, theme switcher, and sample published article.
Checkpoint: a push to the production branch creates a successful production deployment, and a draft still isn't public.
Buy the domain through the registrar you prefer if you don't own it yet. Add that domain to the Vercel project. Vercel will show the DNS record or nameserver change it expects; use the values displayed for your project rather than copying a generic record from a tutorial.
Make the DNS change in your registrar's control panel. Choose whether the apex domain, such as example.com, or www.example.com will be canonical. Configure the other one to redirect to it.
Once Vercel verifies the domain, update the site's configured base URL. Canonical links, RSS, the sitemap, and social-sharing metadata should all use the custom domain.
Checkpoint: both the apex and www addresses use HTTPS, one redirects to the other, and an article's canonical URL points to the version you chose.
Add durable context files for voice, editorial intent, the article schema, available visual components, and publishing rules. Put the canonical copies in the repository. Upload or connect working copies to the LLM Project you will use from your phone.
This is where you describe how you want the conversation to behave: ask questions before outlining, challenge convenient explanations, preserve uncertainty, maintain one canonical draft, and wait for explicit commands before touching the repository.
Checkpoint: start a new conversation and ask it what counts as saving, previewing, and publishing. Its answer should match the versioned policy without needing a reminder.
For a new article, the system should create one branch named draft/<slug>, one post file, and one matching image directory. It should open a draft pull request and return a direct link to the rendered article, not merely the Vercel dashboard.
Protect preview deployments that contain unpublished writing. If the protection available on your hosting plan doesn't meet your needs, treat the draft as potentially accessible and choose a different access-control method before putting sensitive writing there.
Checkpoint: open the preview link on your phone, request a visible change in the conversation, and confirm that a new deployment shows the revision at the same article path.
Record the commit identifier behind the page you reviewed. If the draft changes, the identifier changes and the old approval expires. The next publish request should fail until you review the new preview.
Before publication, the system should also confirm that the branch contains only the article and its images, validation passes, and a production build succeeds.
Checkpoint: approve one preview, change a sentence, and try to publish. The workflow should stop and ask you to review the replacement preview.
Use a disposable article and run the whole loop from your phone:
Chat → Question → Argue → Compose → Preview → Revise → Preview → PostTest both the happy path and the stopping conditions. Say "save this," "preview this," and "looks good" and confirm that none publishes the post. Then review the latest preview and use your explicit publication phrase.
Checkpoint: the production deployment succeeds, the article appears at the custom domain, its feeds and metadata update, and the system returns the verified public URL.
Once that loop works, you can decide how much additional automation you actually need. The point isn't to reproduce my repository exactly. It is to create a path from an unfinished thought to a real page without giving up ownership or the final decision.