Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ grant-application/MAINNET_READINESS_AUDIT.md
grant-application/DOMAIN_SETUP.md
grant-application/TESTER_GUIDE.md
grant-application/TEAM_UPDATE_EMAIL.md
grant-application/DEMO_VIDEO_SCRIPT.md
14 changes: 14 additions & 0 deletions grant-application/CONTENT_SCHEDULE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

**Settlement language rule:** never put the every-few-seconds rhythm and Bitcoin settlement in the same clause. The rhythm is how often the stream updates. Bitcoin finality comes when settlement anchors to Bitcoin, on Bitcoin's own schedule. Say "streams update in seconds; settlement inherits Bitcoin finality" for technical readers, or "your balance updates every few seconds, secured by Bitcoin" in social copy. Never say "settling on Bitcoin every few seconds" or "once it lands, it is final."

**North star:** every post is judged by one question, does it give someone a reason to open a stream? The number we track is total streams created (baseline 9 on Jul 8, 2026; target 40 by Aug 7). Pull it live from `GET /api/stats` (`streamsCreated`) before every metrics post. Full definition in `MARKETING_PLAN_V2.md`.

**CTA rule:** every public post ends with exactly one destination. Default is **stackstream.xyz**. Rotate in the 60 second demo clip or Telegram (**t.me/dev_jaytee**) when they fit the ask better. Official X is @Stackstream0X, personal X is @dev_jayteee, LinkedIn is the founder profile. Forum and Grantees Telegram stay soft ("link in the thread", "DM me"), never salesy. Full handle table in `MARKETING_PLAN_V2.md`.

**Selling points to keep warm (weave in often, never let them go cold):**
1. Settled on Bitcoin. Once a payment is final, it cannot be reversed.
2. Real-time, money moves every few seconds.
Expand Down Expand Up @@ -401,6 +405,16 @@ Forum: 5 substantial threads only. Grantees group: 6 genuine touches. Both kept

---

## July schedule (next)

The June run above shipped without CTAs on most X posts; that is fixed by the CTA rule at the top, which applies to everything from here on. The July schedule gets drafted after two inputs land:
1. The 30 minute engagement review of June's posts, so July is built around the top 2 or 3 formats that actually performed.
2. The team's decision on segment focus (whether to concentrate outreach on Stacks-native DAOs first).

Two assets July will lean on either way: the 60 second demo video (shot-by-shot script kept in the local ops docs) and the weekly Monday metrics post powered by the live `streamsCreated` number.

---

## Notes before posting

- Brackets ([X], [Y], [Z], the spotlight team) need real values. If a DAO or demo streams are not ready by Jun 24, 27, or 29, tell me and I will rework those days so nothing rings hollow.
Expand Down
47 changes: 47 additions & 0 deletions grant-application/MARKETING_PLAN_V2.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,53 @@ This version realigns our marketing to a sharper differentiator. The pivot chang
1. After the Nakamoto upgrade, Stacks blocks arrive about every 5 seconds, so streams update in near real time. Settlement is anchored to Bitcoin, and once anchored it inherits Bitcoin finality.
2. Through sBTC, you can stream actual Bitcoin, continuously.

---

## North star metric

**The one number: total streams created.** Everything we post is judged by one question: does this give someone a reason to open a stream?

| | |
|---|---|
| **Metric** | Total streams created on mainnet (the contract's own counter, `get-stream-nonce`) |
| **Baseline** | 9 streams, as of Jul 8, 2026 (mainnet block 8,508,895) |
| **30-day target** | 40 streams by Aug 7, 2026 (about one new stream a day — team to confirm or adjust) |
| **Where to read it** | `GET /api/stats` on the OpenClaw service returns `streamsCreated` live from the chain. Anyone can verify it against the contract, which is the point. |
| **Secondary (watch, don't chase)** | `workspacesRegistered` from the same endpoint, and unique sender addresses. |

**Why this metric and not another:**
- It is **on-chain and verifiable**. We never have to trust our own spreadsheet, and neither does anyone reading our metrics posts.
- It is **cumulative**, so it never punishes us when a stream completes naturally (unlike "active streams").
- It **doubles as grant evidence** for M2/M3 proof-of-usage without extra work.
- "Teams onboarded" is the outcome we want, but it lags and cannot be verified publicly; streams created is its leading indicator.

**Operating rule:** before drafting any post, name how it could move the number (drives a visit, a demo view, or a direct conversation). If it cannot, it needs a different angle or a different week. The Monday metrics post reports the number weekly, honestly, even when it is flat.

## Call to action rule

**Every public post ends with exactly one CTA.** No post ships with nowhere to go.

| Priority | CTA | Use |
|---|---|---|
| Primary | **stackstream.xyz** | Default on every post: "Try it: stackstream.xyz" |
| Secondary | The 60-second demo clip | When the post's job is belief, not action yet |
| Tertiary | Telegram **t.me/dev_jaytee** | When the ask is "talk to us" (DAO outreach, feedback) |

**Our handles (use these, never improvise):**

| Channel | Handle / link |
|---|---|
| Website | https://stackstream.xyz |
| Official X | @Stackstream0X |
| Personal X (founder) | @dev_jayteee |
| Telegram | t.me/dev_jaytee |
| Discord | dev_jaytee (DM handle — no public server yet; stand one up before using Discord as a post CTA) |
| LinkedIn (founder) | https://www.linkedin.com/in/jethro-irmiya-a2153427b/ |

**Exceptions:** Stacks Forum and the Grantees Telegram stay soft — those channels are for substance and trust, so the CTA there is at most "link in the thread" or "DM me". Selling hard there costs more than it earns.

---

**Settlement language rule (applies to every channel):** never fuse the update cadence and Bitcoin settlement into one clause. The ~5 second rhythm is how often *streams update*; *Bitcoin finality* is inherited when Stacks state anchors to Bitcoin, on Bitcoin's own cadence. Approved shapes: "streams update in seconds; settlement inherits Bitcoin finality" (technical audiences), "your balance updates every few seconds, secured by Bitcoin" (social). Never write "settling on Bitcoin every few seconds/every block" or "once it lands, it is final" — crypto Twitter will (correctly) nitpick both.

---
Expand Down
2 changes: 2 additions & 0 deletions openclaw-service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import daosRouter from "./routes/daos";
import blocksRouter from "./routes/blocks";
import tokensRouter from "./routes/tokens";
import transactionsRouter from "./routes/transactions";
import statsRouter from "./routes/stats";

const app = express();

Expand Down Expand Up @@ -49,6 +50,7 @@ app.use("/api/daos", daosRouter);
app.use("/api/blocks", blocksRouter);
app.use("/api/tokens", tokensRouter);
app.use("/api/tx", transactionsRouter);
app.use("/api/stats", statsRouter);

// Error handler
app.use(errorHandler);
Expand Down
53 changes: 53 additions & 0 deletions openclaw-service/src/routes/stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Router } from "express";
import {
getStreamNonce,
getDaoCount,
getCurrentBlockHeight,
getNetwork,
} from "../stacks-client";

const router = Router();

interface StatsSnapshot {
network: string;
blockHeight: number;
streamsCreated: number;
workspacesRegistered: number;
asOf: string;
}

// The north-star number changes at most once per new stream, so a short cache
// keeps repeated dashboard/marketing polls from burning Hiro quota.
const CACHE_TTL_MS = 60_000;
let cached: { snapshot: StatsSnapshot; expires: number } | null = null;

// GET /api/stats — protocol usage counters, all verifiable on-chain.
// streamsCreated mirrors stream-manager's get-stream-nonce (total streams ever
// opened), the project's north-star growth metric.
router.get("/", async (_req, res, next) => {
try {
if (cached && Date.now() < cached.expires) {
res.json(cached.snapshot);
return;
}
const [streamsCreated, workspacesRegistered, blockHeight] =
await Promise.all([
getStreamNonce(),
getDaoCount(),
getCurrentBlockHeight(),
]);
const snapshot: StatsSnapshot = {
network: getNetwork(),
blockHeight,
streamsCreated,
workspacesRegistered,
asOf: new Date().toISOString(),
};
cached = { snapshot, expires: Date.now() + CACHE_TTL_MS };
res.json(snapshot);
} catch (err) {
next(err);
}
});

export default router;
Loading