<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Stock Market on Producthunt daily</title>
        <link>https://producthunt.programnotes.cn/en/tags/stock-market/</link>
        <description>Recent content in Stock Market on Producthunt daily</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Mon, 21 Sep 2026 21:58:17 +0800</lastBuildDate><atom:link href="https://producthunt.programnotes.cn/en/tags/stock-market/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>OpenStock</title>
        <link>https://producthunt.programnotes.cn/en/p/openstock/</link>
        <pubDate>Mon, 21 Sep 2026 21:58:17 +0800</pubDate>
        
        <guid>https://producthunt.programnotes.cn/en/p/openstock/</guid>
        <description>&lt;img src="https://images.unsplash.com/photo-1564243333038-22ccad2ebef5?ixid=M3w0NjAwMjJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODk5OTg5NDd8&amp;ixlib=rb-4.1.0" alt="Featured image of post OpenStock" /&gt;&lt;h1 id=&#34;open-dev-societyopenstock&#34;&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Open-Dev-Society/OpenStock&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Open-Dev-Society/OpenStock&lt;/a&gt;
&lt;/h1&gt;&lt;p align=&#34;center&#34;&gt;
  &lt;b&gt;New from Open Dev Society:&lt;/b&gt; &lt;a href=&#34;https://github.com/Open-Dev-Society/kitbash&#34;&gt;&lt;b&gt;kitbash&lt;/b&gt;&lt;/a&gt;. Before you build, find out which parts already exist on GitHub. Borrow them, adapt them, or write the rest.
&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;
 &lt;a href=&#34;https://www.star-history.com/open-dev-society/openstock&#34;&gt;
  &lt;picture&gt;&lt;source media=&#34;(prefers-color-scheme: dark)&#34; srcset=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=rank&amp;theme=dark&#34; /&gt;&lt;source media=&#34;(prefers-color-scheme: light)&#34; srcset=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=rank&#34; /&gt;&lt;img alt=&#34;Star History Rank&#34; src=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=rank&#34; /&gt;&lt;/picture&gt; &lt;picture&gt;&lt;source media=&#34;(prefers-color-scheme: dark)&#34; srcset=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=trending&amp;theme=dark&#34; /&gt;&lt;source media=&#34;(prefers-color-scheme: light)&#34; srcset=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=trending&#34; /&gt;&lt;img alt=&#34;GitHub Trending Repository of the Day&#34; src=&#34;https://api.star-history.com/badge?repo=Open-Dev-Society/OpenStock&amp;type=trending&#34; /&gt;&lt;/picture&gt;
 &lt;/a&gt;
&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;
  &lt;a href=&#34;https://trendshift.io/repositories/15728?utm_source=repository-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-repository-15728&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;img src=&#34;https://trendshift.io/api/badge/repositories/15728&#34; alt=&#34;Open-Dev-Society%2FOpenStock | Trendshift&#34; width=&#34;250&#34; height=&#34;55&#34;/&gt;&lt;/a&gt;
  &lt;a href=&#34;https://trendshift.io/repositories/15728?utm_source=trendshift-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-trendshift-15728&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;img src=&#34;https://trendshift.io/api/badge/trendshift/repositories/15728/daily?language=TypeScript&#34; alt=&#34;Open-Dev-Society%2FOpenStock | Trendshift&#34; width=&#34;250&#34; height=&#34;55&#34;/&gt;&lt;/a&gt;
  &lt;a href=&#34;https://trendshift.io/repositories/15728?utm_source=trendshift-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-trendshift-15728&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;img src=&#34;https://trendshift.io/api/badge/trendshift/repositories/15728/weekly?language=TypeScript&#34; alt=&#34;Open-Dev-Society%2FOpenStock | Trendshift&#34; width=&#34;250&#34; height=&#34;55&#34;/&gt;&lt;/a&gt;
  &lt;a href=&#34;https://trendshift.io/repositories/15728?utm_source=trendshift-badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=badge-trendshift-15728&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;img src=&#34;https://trendshift.io/api/badge/trendshift/repositories/15728/monthly?language=TypeScript&#34; alt=&#34;Open-Dev-Society%2FOpenStock | Trendshift&#34; width=&#34;250&#34; height=&#34;55&#34;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div align=&#34;center&#34;&gt;
  &lt;br /&gt;
  &lt;a href=&#34;#&#34; target=&#34;_blank&#34;&gt;
    &lt;img src=&#34;./public/assets/images/dashboard.png&#34; alt=&#34;Project Banner&#34; /&gt;
  &lt;/a&gt;
  © Open Dev Society. This project is licensed under AGPL-3.0; if you modify, redistribute, or deploy it (including as a web service), you must release your source code under the same license and credit the original authors.
  &lt;br /&gt;
  &lt;br/&gt;
  &lt;div&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Next.js-black?style=for-the-badge&amp;logoColor=white&amp;logo=next.js&amp;color=000000&#34; alt=&#34;Next.js badge&#34; /&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-TypeScript-black?style=for-the-badge&amp;logoColor=white&amp;logo=typescript&amp;color=3178C6&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Tailwind%20CSS-black?style=for-the-badge&amp;logoColor=white&amp;logo=tailwindcss&amp;color=38B2AC&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-shadcn/ui-black?style=for-the-badge&amp;logoColor=white&amp;logo=shadcnui&amp;color=000000&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Radix%20UI-black?style=for-the-badge&amp;logoColor=white&amp;logo=radixui&amp;color=000000&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Better%20Auth-black?style=for-the-badge&amp;logoColor=white&amp;logo=betterauth&amp;color=000000&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-MongoDB-black?style=for-the-badge&amp;logoColor=white&amp;logo=mongodb&amp;color=00A35C&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Inngest-black?style=for-the-badge&amp;logoColor=white&amp;logo=inngest&amp;color=000000&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Nodemailer-black?style=for-the-badge&amp;logoColor=white&amp;logo=gmail&amp;color=EA4335&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-TradingView-black?style=for-the-badge&amp;logoColor=white&amp;logo=tradingview&amp;color=2962FF&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-Finnhub-black?style=for-the-badge&amp;logoColor=white&amp;color=30B27A&#34;/&gt;
    &lt;img src=&#34;https://img.shields.io/badge/-CodeRabbit-black?style=for-the-badge&amp;logoColor=white&amp;logo=coderabbit&amp;color=9146FF&#34;/&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h1 id=&#34;openstock&#34;&gt;OpenStock
&lt;/h1&gt;&lt;p&gt;OpenStock is an open-source alternative to expensive market platforms. Track real-time prices, set personalized alerts, and explore detailed company insights — built openly, for everyone, forever free.&lt;/p&gt;
&lt;p&gt;Note: OpenStock is community-built and not a brokerage. Market data may be delayed based on provider rules and your configuration. Nothing here is financial advice.&lt;/p&gt;
&lt;h2 id=&#34;-table-of-contents&#34;&gt;📋 Table of Contents
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;✨ &lt;a class=&#34;link&#34; href=&#34;#introduction&#34; &gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🌍 &lt;a class=&#34;link&#34; href=&#34;#manifesto&#34; &gt;Open Dev Society Manifesto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;⚙️ &lt;a class=&#34;link&#34; href=&#34;#tech-stack&#34; &gt;Tech Stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔋 &lt;a class=&#34;link&#34; href=&#34;#features&#34; &gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🤸 &lt;a class=&#34;link&#34; href=&#34;#quick-start&#34; &gt;Quick Start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🐳 &lt;a class=&#34;link&#34; href=&#34;#docker-setup&#34; &gt;Docker Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔐 &lt;a class=&#34;link&#34; href=&#34;#environment-variables&#34; &gt;Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧱 &lt;a class=&#34;link&#34; href=&#34;#project-structure&#34; &gt;Project Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📡 &lt;a class=&#34;link&#34; href=&#34;#data--integrations&#34; &gt;Data &amp;amp; Integrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🌍 &lt;a class=&#34;link&#34; href=&#34;#market-support&#34; &gt;Market Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🧪 &lt;a class=&#34;link&#34; href=&#34;#scripts--tooling&#34; &gt;Scripts &amp;amp; Tooling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🤝 &lt;a class=&#34;link&#34; href=&#34;#contributing&#34; &gt;Contributing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🛡️ &lt;a class=&#34;link&#34; href=&#34;#security&#34; &gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📜 &lt;a class=&#34;link&#34; href=&#34;#license&#34; &gt;License&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🙏 &lt;a class=&#34;link&#34; href=&#34;#acknowledgements&#34; &gt;Acknowledgements&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;-introduction&#34;&gt;✨ Introduction &lt;a name=&#34;introduction&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;OpenStock is a modern stock market app powered by Next.js (App Router), shadcn/ui and Tailwind CSS, Better Auth for authentication, MongoDB for persistence, Finnhub for market data, and TradingView widgets for charts and market views.&lt;/p&gt;
&lt;h2 id=&#34;-open-dev-society-manifesto&#34;&gt;🌍 Open Dev Society Manifesto &lt;a name=&#34;manifesto&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;We live in a world where knowledge is hidden behind paywalls. Where tools are locked in subscriptions. Where information is twisted by bias. Where newcomers are told they’re not “good enough” to build.&lt;/p&gt;
&lt;p&gt;We believe there’s a better way.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our Belief: Technology should belong to everyone. Knowledge should be open, free, and accessible. Communities should welcome newcomers with trust, not gatekeeping.&lt;/li&gt;
&lt;li&gt;Our Mission: Build free, open-source projects that make a real difference:
&lt;ul&gt;
&lt;li&gt;Tools that professionals and students can use without barriers.&lt;/li&gt;
&lt;li&gt;Knowledge platforms where learning is free, forever.&lt;/li&gt;
&lt;li&gt;Communities where every beginner is guided, not judged.&lt;/li&gt;
&lt;li&gt;Resources that run on trust, not profit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Our Promise: We will never lock knowledge. We will never charge for access. We will never trade trust for money. We run on transparency, donations, and the strength of our community.&lt;/li&gt;
&lt;li&gt;Our Call: If you’ve ever felt you didn’t belong, struggled to find free resources, or wanted to build something meaningful — you belong here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because the future belongs to those who build it openly.&lt;/p&gt;
&lt;h2 id=&#34;-tech-stack&#34;&gt;⚙️ Tech Stack &lt;a name=&#34;tech-stack&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Core&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Next.js 15 (App Router), React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS v4 (via @tailwindcss/postcss)&lt;/li&gt;
&lt;li&gt;shadcn/ui + Radix UI primitives&lt;/li&gt;
&lt;li&gt;Lucide icons&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Auth &amp;amp; Data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better Auth (email/password) with MongoDB adapter&lt;/li&gt;
&lt;li&gt;MongoDB + Mongoose&lt;/li&gt;
&lt;li&gt;Finnhub API for symbols, profiles, and market news&lt;/li&gt;
&lt;li&gt;TradingView embeddable widgets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Automation &amp;amp; Comms&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inngest (events, cron, AI inference via Gemini)&lt;/li&gt;
&lt;li&gt;Nodemailer (Gmail transport)&lt;/li&gt;
&lt;li&gt;next-themes, cmdk (command palette), react-hook-form&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Language composition&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript (~93.4%), CSS (~6%), JavaScript (~0.6%)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-features&#34;&gt;🔋 Features &lt;a name=&#34;features&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Authentication
&lt;ul&gt;
&lt;li&gt;Email/password auth with Better Auth + MongoDB adapter&lt;/li&gt;
&lt;li&gt;Protected routes enforced via Next.js middleware&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Global search and Command + K palette
&lt;ul&gt;
&lt;li&gt;Fast stock search backed by Finnhub&lt;/li&gt;
&lt;li&gt;Popular stocks when idle; debounced querying&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Watchlist
&lt;ul&gt;
&lt;li&gt;Per-user watchlist stored in MongoDB (unique symbol per user)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stock details
&lt;ul&gt;
&lt;li&gt;TradingView symbol info, candlestick/advanced charts, baseline, technicals&lt;/li&gt;
&lt;li&gt;Company profile and financials widgets&lt;/li&gt;
&lt;li&gt;Optional cross-source sentiment insights for Reddit, X.com, news, and Polymarket&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Market overview
&lt;ul&gt;
&lt;li&gt;Heatmap, quotes, and top stories (TradingView widgets)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Personalized onboarding
&lt;ul&gt;
&lt;li&gt;Collects country, investment goals, risk tolerance, preferred industry&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Email &amp;amp; automation
&lt;ul&gt;
&lt;li&gt;AI-personalized welcome email (Gemini via Inngest)&lt;/li&gt;
&lt;li&gt;Weekly news summary email (cron) sent as a Kit broadcast&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polished UI
&lt;ul&gt;
&lt;li&gt;shadcn/ui components, Radix primitives, Tailwind v4 design tokens&lt;/li&gt;
&lt;li&gt;Dark theme by default&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Keyboard shortcut
&lt;ul&gt;
&lt;li&gt;Cmd/Ctrl + K for quick actions/search&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-quick-start&#34;&gt;🤸 Quick Start &lt;a name=&#34;quick-start&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Prerequisites&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 20+ and pnpm or npm&lt;/li&gt;
&lt;li&gt;MongoDB connection string (MongoDB Atlas or local via Docker Compose)&lt;/li&gt;
&lt;li&gt;Finnhub API key (free tier supported; real-time may require paid)&lt;/li&gt;
&lt;li&gt;Gmail account for email (or update Nodemailer transport)&lt;/li&gt;
&lt;li&gt;Optional: Google Gemini API key (for AI-generated welcome intros)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clone and install&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/Open-Dev-Society/OpenStock.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; OpenStock
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# choose one:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pnpm install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Configure environment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;.env&lt;/code&gt; file (see &lt;a class=&#34;link&#34; href=&#34;#environment-variables&#34; &gt;Environment Variables&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Verify DB connectivity:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pnpm test:db
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run test:db
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Run development&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Next.js dev (Turbopack)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pnpm dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Run Inngest locally (workflows, cron, AI)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npx inngest-cli@latest dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Build &amp;amp; start (production)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pnpm build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Open http://localhost:3000 to view the app.&lt;/p&gt;
&lt;h2 id=&#34;-docker-setup&#34;&gt;🐳 Docker Setup &lt;a name=&#34;docker-setup&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;You can run OpenStock and MongoDB easily with Docker Compose.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Ensure Docker and Docker Compose are installed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;docker-compose.yml includes two services:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;openstock (this app)&lt;/li&gt;
&lt;li&gt;mongodb (MongoDB database with a persistent volume)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Create your &lt;code&gt;.env&lt;/code&gt; (see examples below). For the Docker setup, use a local connection string like:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-env&#34; data-lang=&#34;env&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MONGODB_URI&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mongodb://root:example@mongodb:27017/openstock?authSource&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;admin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Start the stack:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# from the repository root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose up -d mongodb &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose up -d --build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;Access the app:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;App: http://localhost:3000&lt;/li&gt;
&lt;li&gt;MongoDB is available inside the Docker network at host mongodb:27017&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The app service depends_on the mongodb service.&lt;/li&gt;
&lt;li&gt;Credentials are defined in Compose for the MongoDB root user; authSource=admin is required on the connection string for root.&lt;/li&gt;
&lt;li&gt;Data persists across restarts via the docker volume.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Optional: Example MongoDB service definition used in this project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mongodb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mongo:7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mongodb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;restart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;unless-stopped&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;example&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;27017:27017&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;mongo-data:/data/db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;healthcheck&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;CMD&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;mongosh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;--eval&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;db.adminCommand(&amp;#39;ping&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;10s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;retries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mongo-data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;-environment-variables&#34;&gt;🔐 Environment Variables &lt;a name=&#34;environment-variables&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Create &lt;code&gt;.env&lt;/code&gt; at the project root. Choose either a hosted MongoDB (Atlas) URI or the local Docker URI.&lt;/p&gt;
&lt;p&gt;Hosted (MongoDB Atlas):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-env&#34; data-lang=&#34;env&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Core&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Database (Atlas)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MONGODB_URI&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mongodb+srv://&amp;lt;user&amp;gt;:&amp;lt;pass&amp;gt;@&amp;lt;cluster&amp;gt;/&amp;lt;db&amp;gt;?retryWrites&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;true&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;majority
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Better Auth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BETTER_AUTH_SECRET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_better_auth_secret
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BETTER_AUTH_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;http://localhost:3000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Finnhub&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Note: NEXT_PUBLIC_FINNHUB_API_KEY is required for Vercel deployment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NEXT_PUBLIC_FINNHUB_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_finnhub_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;FINNHUB_BASE_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;https://finnhub.io/api/v1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Sentiment insights (optional)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ADANOS_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_adanos_api_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ADANOS_API_BASE_URL=https://api.adanos.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# AI Provider (optional, default: &amp;#34;gemini&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Supported: &amp;#34;gemini&amp;#34;, &amp;#34;minimax&amp;#34;, &amp;#34;siray&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# AI_PROVIDER=gemini&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Gemini&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_gemini_api_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# MiniMax (optional, used when AI_PROVIDER=minimax or as fallback)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get your key at https://platform.minimaxi.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# MINIMAX_API_KEY=your_minimax_api_key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Inngest Signing Key (required for Vercel deployment)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get this from your Inngest dashboard: https://app.inngest.com/env/settings/keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;INNGEST_SIGNING_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_inngest_signing_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Email (Nodemailer via Gmail; consider App Passwords if 2FA)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODEMAILER_EMAIL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;youraddress@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODEMAILER_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_gmail_app_password
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Local (Docker Compose) MongoDB:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-env&#34; data-lang=&#34;env&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Core&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;development
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Database (Docker)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MONGODB_URI&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mongodb://root:example@mongodb:27017/openstock?authSource&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Better Auth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BETTER_AUTH_SECRET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_better_auth_secret
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BETTER_AUTH_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;http://localhost:3000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Finnhub&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Note: NEXT_PUBLIC_FINNHUB_API_KEY is required for Vercel deployment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NEXT_PUBLIC_FINNHUB_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_finnhub_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;FINNHUB_BASE_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;https://finnhub.io/api/v1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Sentiment insights (optional)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ADANOS_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_adanos_api_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ADANOS_API_BASE_URL=https://api.adanos.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# AI Provider (optional, default: &amp;#34;gemini&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Supported: &amp;#34;gemini&amp;#34;, &amp;#34;minimax&amp;#34;, &amp;#34;siray&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# AI_PROVIDER=gemini&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Gemini&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_gemini_api_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# MiniMax (optional, used when AI_PROVIDER=minimax or as fallback)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get your key at https://platform.minimaxi.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# MINIMAX_API_KEY=your_minimax_api_key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Inngest Signing Key (required for Vercel deployment)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get this from your Inngest dashboard: https://app.inngest.com/env/settings/keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;INNGEST_SIGNING_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_inngest_signing_key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Email (Nodemailer via Gmail; consider App Passwords if 2FA)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODEMAILER_EMAIL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;youraddress@gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NODEMAILER_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;your_gmail_app_password
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Notes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep private keys server-side whenever possible.&lt;/li&gt;
&lt;li&gt;If using &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; variables, remember they are exposed to the browser.&lt;/li&gt;
&lt;li&gt;In production, prefer a dedicated SMTP provider over a personal Gmail.&lt;/li&gt;
&lt;li&gt;Do not hardcode secrets in the Dockerfile; use &lt;code&gt;.env&lt;/code&gt; and Compose.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-project-structure&#34;&gt;🧱 Project Structure &lt;a name=&#34;project-structure&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sign&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;up&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;stocks&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inngest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;globals&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;css&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;layout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;components&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;ui&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# shadcn/radix primitives (button, dialog, command, input, etc.)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;forms&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;# InputField, SelectField, CountrySelectField, FooterLink&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Header&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Footer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SearchCommand&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WatchlistButton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tsx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;models&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;watchlist&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;model&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;mongoose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;actions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# server actions (auth, finnhub, user, watchlist)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;better&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auth&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;inngest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;# client, functions, prompts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;nodemailer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;…&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# transporter, email templates&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;constants&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;utils&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;scripts&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mjs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;types&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;global&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ts&lt;/span&gt;          &lt;span class=&#34;c1&#34;&gt;# i.ibb.co image domain allowlist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;postcss&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mjs&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;# Tailwind v4 postcss setup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;components&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;# shadcn config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assets&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;images&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# logos and screenshots&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;-data--integrations&#34;&gt;📡 Data &amp;amp; Integrations &lt;a name=&#34;data--integrations&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Finnhub&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stock search, company profiles, and market news.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;NEXT_PUBLIC_FINNHUB_API_KEY&lt;/code&gt; and &lt;code&gt;FINNHUB_BASE_URL&lt;/code&gt; (default: &lt;a class=&#34;link&#34; href=&#34;https://finnhub.io/api/v1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://finnhub.io/api/v1&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Free tiers may return delayed quotes; respect rate limits and terms.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adanos sentiment insights (optional)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Structured stock sentiment snapshots across Reddit, X.com, news, and Polymarket.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;ADANOS_API_KEY&lt;/code&gt;; optionally override the API host with &lt;code&gt;ADANOS_API_BASE_URL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Used only for the stock detail sentiment card and does not replace Finnhub or TradingView.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TradingView&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Embeddable widgets used for charts, heatmap, quotes, and timelines.&lt;/li&gt;
&lt;li&gt;External images from &lt;code&gt;i.ibb.co&lt;/code&gt; are allowlisted in &lt;code&gt;next.config.ts&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Better Auth + MongoDB&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email/password with MongoDB adapter.&lt;/li&gt;
&lt;li&gt;Session validation via middleware; most routes are protected, with public exceptions for &lt;code&gt;sign-in&lt;/code&gt;, &lt;code&gt;sign-up&lt;/code&gt;, assets and Next internals.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inngest&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Workflows:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/user.created&lt;/code&gt; → AI-personalized Welcome Email&lt;/li&gt;
&lt;li&gt;Cron &lt;code&gt;0 9 * * 1&lt;/code&gt; → Weekly News Summary (every Monday at 9 AM, Kit broadcast)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Local dev: &lt;code&gt;npx inngest-cli@latest dev&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Email (Nodemailer)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gmail transport. Update credentials or switch to your SMTP provider.&lt;/li&gt;
&lt;li&gt;Templates for welcome and news summary emails.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-market-support&#34;&gt;🌍 Market Support &lt;a name=&#34;market-support&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;OpenStock supports &lt;strong&gt;30+ international stock exchanges&lt;/strong&gt; including NSE, LSE, TSX, and more. However, please be aware of important limitations based on our data providers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick Facts&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Finnhub supports most global exchanges&lt;/li&gt;
&lt;li&gt;⚠️ TradingView free tier has restrictions on emerging markets (India NSE, Vietnam, etc.)&lt;/li&gt;
&lt;li&gt;📊 Real-time data for non-US stocks is delayed by 15+ minutes on free tier&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;See &lt;a class=&#34;link&#34; href=&#34;./MARKET_SUPPORT.md&#34; &gt;MARKET_SUPPORT.md&lt;/a&gt; for&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complete list of supported exchanges&lt;/li&gt;
&lt;li&gt;Known limitations and workarounds&lt;/li&gt;
&lt;li&gt;Why &amp;ldquo;This symbol is only available on TradingView&amp;rdquo; appears&lt;/li&gt;
&lt;li&gt;How to upgrade for broader market coverage&lt;/li&gt;
&lt;li&gt;Future improvements planned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the latest supported symbols and exchanges, see &lt;a class=&#34;link&#34; href=&#34;https://finnhub.io/docs/api/symbol-lookup&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Finnhub&amp;rsquo;s exchange list&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;-scripts--tooling&#34;&gt;🧪 Scripts &amp;amp; Tooling &lt;a name=&#34;scripts--tooling&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Package scripts&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dev&lt;/code&gt;: Next.js dev server with Turbopack&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build&lt;/code&gt;: Production build (Turbopack)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt;: Run production server&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lint&lt;/code&gt;: ESLint&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test:db&lt;/code&gt;: Validate DB connectivity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Developer experience&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript strict mode&lt;/li&gt;
&lt;li&gt;Tailwind CSS v4 (no separate tailwind.config needed)&lt;/li&gt;
&lt;li&gt;shadcn/ui components with Radix primitives&lt;/li&gt;
&lt;li&gt;cmdk command palette, next-themes, lucide-react icons&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-contributing&#34;&gt;🤝 Contributing &lt;a name=&#34;contributing&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;You belong here. Whether you’re a student, a self-taught dev, or a seasoned engineer — contributions are welcome.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open an issue to discuss ideas and bugs&lt;/li&gt;
&lt;li&gt;Look for “good first issue” or “help wanted”&lt;/li&gt;
&lt;li&gt;Keep PRs focused; add screenshots for UI changes&lt;/li&gt;
&lt;li&gt;Be kind, guide beginners, no gatekeeping — that’s the ODS way&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-security&#34;&gt;🛡️ Security &lt;a name=&#34;security&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;If you discover a vulnerability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not open a public issue&lt;/li&gt;
&lt;li&gt;Email: &lt;a href=&#34;mailto:opendevsociety@cc.cc&#34;&gt;&lt;a class=&#34;link&#34; href=&#34;mailto:opendevsociety@cc.cc&#34; &gt;opendevsociety@cc.cc&lt;/a&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;ll coordinate responsible disclosure and patch swiftly&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-license&#34;&gt;📜 License &lt;a name=&#34;license&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;OpenStock is and will remain free and open for everyone. This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.&lt;/p&gt;
&lt;h2 id=&#34;-acknowledgements&#34;&gt;🙏 Acknowledgements &lt;a name=&#34;acknowledgements&#34;&gt;&lt;/a&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Finnhub for accessible market data&lt;/li&gt;
&lt;li&gt;TradingView for embeddable market widgets&lt;/li&gt;
&lt;li&gt;shadcn/ui, Radix UI, Tailwind CSS, Next.js community&lt;/li&gt;
&lt;li&gt;Inngest for dependable background jobs and workflows&lt;/li&gt;
&lt;li&gt;Better Auth for simple and secure authentication&lt;/li&gt;
&lt;li&gt;All contributors who make open tools possible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;— Built openly, for everyone, forever free. Open Dev Society.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;© Open Dev Society. This project is licensed under AGPL-3.0; if you modify, redistribute, or deploy it (including as a web service), you must release your source code under the same license and credit the original authors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;our-honourable-contributors&#34;&gt;Our Honourable Contributors
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ravixalgorithm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ravixalgorithm&lt;/a&gt; - Developed the entire application from the ground up, including authentication, UI design, API and AI integration, and deployment.&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Priyanshuu00007&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Priyanshuu00007&lt;/a&gt; - Created the official OpenStock logo and contributed to the project’s visual identity.&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/chinnsenn&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;chinnsenn&lt;/a&gt; - Set up Docker configuration for the repository, ensuring a smooth development and deployment process.&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/koevoet1221&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;koevoet1221&lt;/a&gt; - Resolved MongoDB Docker build issues, improving the project’s overall stability and reliability.&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ettoreciolli1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ettoreciolli1&lt;/a&gt; - updated Readme&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-partners--backers&#34;&gt;❤️ Partners &amp;amp; Backers
&lt;/h2&gt;&lt;a href=&#34;https://www.siray.ai/&#34;&gt;
  &lt;img src=&#34;public/assets/icons/siray.svg&#34; alt=&#34;Siray.ai Logo&#34; width=&#34;100&#34; /&gt;
&lt;/a&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.siray.ai/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Siray.ai&lt;/a&gt;&lt;/strong&gt; — The robust AI infrastructure backing OpenStock. Siray.ai ensures our market insights never sleep.&lt;/p&gt;
&lt;h2 id=&#34;special-thanks&#34;&gt;Special thanks
&lt;/h2&gt;&lt;p&gt;Huge thanks to &lt;a class=&#34;link&#34; href=&#34;https://github.com/adrianhajdin&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Adrian Hajdin (JavaScript Mastery)&lt;/a&gt; — his excellent Stock Market App tutorial was instrumental in building OpenStock for the open-source community under the Open Dev Society.&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a class=&#34;link&#34; href=&#34;https://github.com/adrianhajdin&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;adrianhajdin&lt;/a&gt;
YouTube tutorial: &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=gu4pafNCXng&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Stock Market App Tutorial&lt;/a&gt;
YouTube channel: &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/@javascriptmastery&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;JavaScript Mastery&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
