Everything you need to get this into both stores. Do Firefox first — it is free, faster, and its reviewers catch things Chrome’s do not.
For Chrome, no. For Firefox, yes.
Chrome only runs extensions Google has signed, and Google only signs what goes
through the Web Store. Loading a .crx from a file or your own server has been
blocked since Chrome 33 on Windows and Chrome 44 on macOS. What is left:
| Route | Who it reaches |
|---|---|
| Chrome Web Store — Public | Anyone. Searchable. |
| Chrome Web Store — Unlisted | Anyone with the direct link. Not searchable. Same $5, same review. |
| Load unpacked, Developer mode | Only people willing to clone, build, and re-enable it. Chrome nags at every startup. |
| Enterprise policy force-install | Only machines your organisation manages. |
So “download it from GitHub” is a developer distribution channel, not a user one.
Anyone who is not comfortable with chrome://extensions will not install it.
This is why devo is in the store rather than telling people to build it — and it
is the same reason to publish TrendABC there.
Unlisted is the useful middle step. Pay the $5, submit, publish unlisted, and you get a real install link to hand to a few people before going public. The review still happens, so it also flushes out permission objections early.
Firefox is genuinely different. Mozilla will sign an add-on you then host yourself:
# Get API credentials at addons.mozilla.org/developers/addon/api/key/
npx web-ext sign --source-dir .output/firefox-mv3 \
--api-key "$AMO_JWT_ISSUER" --api-secret "$AMO_JWT_SECRET" \
--channel unlisted
That returns a signed .xpi you can attach to a GitHub release, and users install
it straight from there. It still passes through Mozilla’s signing, but it never
appears in the AMO catalogue. --channel listed publishes it publicly instead.
Unsigned add-ons only load in Firefox Developer Edition, Nightly, or ESR with
xpinstall.signatures.required disabled — not in normal Firefox.
pnpm zip # → .output/trendabc-0.1.0-chrome.zip
pnpm zip:firefox # → .output/trendabc-0.1.0-firefox.zip
# + .output/trendabc-0.1.0-sources.zip
Before either submission, bump version in package.json. Both stores reject a
version number that has already been uploaded, and neither lets you reuse one
after a rejection.
Free. No registration fee. Usually reviewed within a day or two.
trendabc-0.1.0-firefox.ziptrendabc-0.1.0-sources.zip when asked for source code. This is
required, not optional: the build is bundled by Vite, so reviewers cannot read
the shipped file. Skipping it gets you rejected.Build instructions for the reviewer — paste this:
Node 22, pnpm 10.
pnpm install --frozen-lockfile
pnpm build:firefox
Output: .output/firefox-mv3
The manifest already declares browser_specific_settings.gecko.id and
data_collection_permissions, both of which AMO requires. Confirm zero errors
before you upload:
npx web-ext lint --source-dir .output/firefox-mv3
Updating later: bump the version, pnpm zip:firefox, upload both zips again
under the same add-on.
Budget: $5 once, about 40 minutes of form filling, then days to a couple of weeks of waiting. The permissions here put this at the slower end.
These are the things that actually get extensions rejected. All but two are already done.
alarms was declared and never called; it has
been removed. Reviewers check this and reject for it.public/fonts/. The new tab
makes zero third-party requests at load.public/.store/screenshots/ — regenerate
any time with pnpm shots.PRIVACY.md exists; it needs to be
reachable over HTTPS. Easiest: push the repo public, enable GitHub Pages,
and use https://abhishek-ch.github.io/trendabc/PRIVACY. A raw GitHub URL works
too.REPO in entrypoints/newtab/main.js,
and repository / homepage / bugs in package.json.Then build the upload:
pnpm test # must be green
pnpm build
pnpm zip # → .output/trendabc-0.1.0-chrome.zip
Set and verify the publisher contact email. This is not on the item’s pages — it lives on the developer Account page, and until it is done every item shows two blocking errors:
You must provide a contact email before you can publish any item. You must verify the publisher’s contact email before you can publish any item.
Fix it once, for the whole account:
This address is published on your listing, so use one you are happy to show publicly. It is where users and Google will contact you about the extension; it does not have to be the Google account you signed in with.
.output/trendabc-0.1.0-chrome.zip| Field | What to enter |
|---|---|
| Name | TrendABC |
| Summary | What's trending on GitHub, Hacker News, Reddit and arXiv — in one tab. Summarise anything with your own AI key. |
| Description | the block under Store listing copy below |
| Category | Productivity |
| Language | English |
Additional fields — all three are optional, but fill the last two. A listing with no support link looks abandoned before anyone installs it.
| Field | What to enter |
|---|---|
| Official URL | None. This dropdown only lists domains you have verified you own, under Account → Verified domains. Skip it unless you own a domain and want it shown as verified. A github.com URL can never appear here — you do not own github.com. |
| Homepage URL | https://github.com/abhishek-ch/trendabc |
| Support URL | https://github.com/abhishek-ch/trendabc/issues |
Screenshots — upload all five from store/screenshots/, in this order. The
first is what people see in search results, so it leads with the product:
1-boards.png — the columns2-signal-ranked.png — merged and ranked3-summary.png — a summary streaming in4-dark.png — dark theme5-settings.png — bring your own keyStore icon — 128×128, use public/icon-128.png.
Small promo tile — 440×280, optional. It affects whether you can be featured. Skip it for v0.1.
Single purpose — paste exactly:
Replace the new tab page with a view of what is currently trending across developer news sources, and let the user summarise or ask questions about any item using their own AI provider API key.
Permission justifications — one box per permission. Copy these:
| Permission | Paste this |
|---|---|
storage |
Stores the user’s settings, cached source listings, and saved summaries locally on their device. |
unlimitedStorage |
Saved summaries accumulate without bound over time and would otherwise hit the default quota. |
identity |
Optional Reddit sign-in through Reddit’s own OAuth consent screen, so the user can read their subscribed and private subreddits. Never invoked unless the user explicitly chooses to connect an account. |
declarativeNetRequestWithHostAccess |
Removes the cross-origin Origin header from requests to reddit.com only, because Reddit’s bot filter rejects requests carrying it. The rule is static, declared in reddit-rules.json, and matches no other host. |
| Host permission (one box only — see below) | The console gives a single box for all hosts. It must cover the news sources, the AI providers, and the optional wildcard together. Covering only the news sources is a mismatch against the manifest, and mismatches get rejected. |
Host permission justification — paste this whole block into the single box:
The extension reads public listings from the news sources it displays:
github.com, hn.algolia.com, reddit.com, lobste.rs, export.arxiv.org and
huggingface.co. That access is read-only and the content is public.
It also needs api.anthropic.com, api.openai.com and openrouter.ai. These are
the AI providers the user chooses between. Requests are made only with the
user's own API key, and only when the user clicks summarise or sends a
message. No key is bundled with the extension and there is no backend server
of our own.
The broad optional host permission (*://*/*) is never requested at install.
It is requested at runtime, one origin at a time, only when the user asks for
a summary of a specific article, so the extension can read that one page's
text.
Are you using remote code? — No. True only because the fonts are
self-hosted in public/fonts/. If you ever re-add a <link> to Google Fonts or
any CDN script, this answer becomes a false declaration.
Data usage — declare exactly these two, and nothing else:
Leave everything else unticked. No personally identifiable information, no location, no health, no financial, no personal communications, no web history, no user activity.
Then tick all three certifications — all three are true here:
Privacy policy URL — required because you declared data collection above.
Paste the public URL of PRIVACY.md.
Do not claim you collect nothing. The extension stores API keys and transmits page text. Declaring that plainly is what passes review; a mismatch between your declaration and what the code does is the fastest rejection.
Flip to Public later from the same tab; that flip needs another short review.
Clicking Submit for review opens a confirmation with one checkbox:
☑ Publish “TrendABC” automatically after it has passed review
Leave it ticked if visibility is Unlisted — approval then puts it live at a link only you have handed out. Untick it if visibility is Public and you want to choose the moment it appears in the store.
The dialog also suggests narrower permissions shorten review. That is aimed at
*://*/*, which is already optional_host_permissions and requested on click
rather than at install. It cannot be narrowed further without dropping article
summarising, so expect the slower end of the range.
*://*/*
permission can push it to a couple of weeks.package.json, pnpm zip, and upload again. A version number
that has already been submitted cannot be reused.https://chromewebstore.google.com/detail/trendabc/<your-extension-id>. Put
it at the top of the README.| Question | Answer |
|---|---|
| Why override the new tab page? | That is the product. The content is the new tab. |
| Why request access to all sites? | Optionally, at runtime, only to read the article the user asked to summarise. It is not in host_permissions; it is optional_host_permissions and requested on click. |
| Is there a backend? | No. No server, no analytics, no telemetry. Every request goes from the user’s browser to the source or to their own AI provider. |
| Whose API key is used? | The user’s. None is bundled. Without a key, the summarise and chat features simply do not run. |
| Why modify request headers? | One static rule stripping Origin for reddit.com, because Reddit rejects requests carrying it. No other host is touched, and nothing is blocked or redirected. |
Same text works for both.
Name: TrendABC
Summary (132 chars max):
What’s trending on GitHub, Hacker News, Reddit and arXiv — in one tab. Summarise anything with your own AI key.
Description:
TrendABC turns your new tab into a view of what is happening across developer news, right now.
BOARDS — one column per source: GitHub Trending, Hacker News, Show HN, Lobsters, Reddit, arXiv, and any RSS feed you add. Close a column and the rest share the width. Add one and they divide again.
SIGNAL — every board merged, deduplicated and ranked, so a story appearing on several sources at once rises to the top and is marked corroborated.
ASK ANYTHING — click any item to summarise it or ask questions about it, using your own API key for Anthropic, OpenAI or OpenRouter. Summaries are cached forever and free to reopen. Your key is stored locally and is sent to nobody but the provider you chose.
No account required. No server. No tracking. No telemetry. Open source, MIT licensed.
Category: Productivity (Chrome) · Other (Firefox)
Assets — all generated, nothing left to draw:
store/screenshots/. Rebuild any
time with pnpm shots — it drives the real extension, stubs only the AI call,
and resamples from 2× so the text stays sharp.public/.REPO in entrypoints/newtab/main.js points at the real repositoryrepository, homepage and bugs in package.json are real URLsREADME.md is realversion bumped in package.jsonpnpm test passesnpx web-ext lint --source-dir .output/firefox-mv3 reports zero errorsPRIVACY.md published somewhere with a stable URL