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
6 changes: 3 additions & 3 deletions cmd/cosift/community.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func runCommunity(ctx context.Context, args []string) error {
backend := fs.String("backend", "http://127.0.0.1:7777", "Cosift Pebble server origin")
dir := fs.String("data-dir", "./community-data", "private account database directory")
proxies := fs.String("trusted-proxies", "", "comma-separated proxy CIDRs allowed to supply X-Forwarded-For")
guestInterval := fs.Duration("guest-interval", time.Minute, "shared guest allowance interval")
freeRPM := fs.Int("member-free-rpm", 60, "shared free member requests per minute")
guestInterval := fs.Duration("guest-interval", 30*time.Minute, "guest Search cooldown; Answer uses twice this interval and Research three times, shared across modes")
freeRPM := fs.Int("member-free-rpm", 0, "deprecated and ignored; every successful authenticated request costs credits")
searchRPM := fs.Int("search-rpm", 120, "member Search hard cap per minute, including credit requests")
answerRPM := fs.Int("answer-rpm", 20, "member Answer hard cap per minute, including credit requests")
researchLimit := fs.Int("research-per-10m", 3, "member Research hard cap per ten minutes, including credit requests")
Expand All @@ -56,7 +56,7 @@ func runCommunity(ctx context.Context, args []string) error {
defer client.Close()
provider = client
}
s, err := community.Open(community.Config{GAMeasurementID: os.Getenv("COSIFT_GA_MEASUREMENT_ID"), Shared: provider, DataDir: *dir, Backend: *backend, PublicURL: *publicURL, AdminToken: os.Getenv("COSIFT_COMMUNITY_ADMIN_TOKEN"), TrustedProxies: trusted, GuestInterval: *guestInterval, MemberFreeRPM: *freeRPM, SearchRPM: *searchRPM, AnswerRPM: *answerRPM, ResearchPer10Min: *researchLimit, StripeSecretKey: os.Getenv("STRIPE_SECRET_KEY"), StripeWebhookSecret: os.Getenv("STRIPE_WEBHOOK_SECRET"), AllowTestPayments: os.Getenv("COSIFT_ALLOW_TEST_PAYMENTS") == "1", StripePortalConfigurationID: os.Getenv("COSIFT_STRIPE_PORTAL_CONFIGURATION_ID")})
s, err := community.Open(community.Config{GAMeasurementID: os.Getenv("COSIFT_GA_MEASUREMENT_ID"), Shared: provider, SharedPasswordEnabled: os.Getenv("COSIFT_SHARED_PASSWORD_ENABLED") == "1", DataDir: *dir, Backend: *backend, PublicURL: *publicURL, AdminToken: os.Getenv("COSIFT_COMMUNITY_ADMIN_TOKEN"), TrustedProxies: trusted, GuestInterval: *guestInterval, MemberFreeRPM: *freeRPM, SearchRPM: *searchRPM, AnswerRPM: *answerRPM, ResearchPer10Min: *researchLimit, StripeSecretKey: os.Getenv("STRIPE_SECRET_KEY"), StripeWebhookSecret: os.Getenv("STRIPE_WEBHOOK_SECRET"), AllowTestPayments: os.Getenv("COSIFT_ALLOW_TEST_PAYMENTS") == "1", StripePortalConfigurationID: os.Getenv("COSIFT_STRIPE_PORTAL_CONFIGURATION_ID")})
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions deploy/community.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ COSIFT_ALLOW_TEST_PAYMENTS=0

# Default: standalone email/password accounts. See docs/SHARED-ACCOUNTS.md.
COSIFT_AUTH_MODE=local
# Opt in only after cosift-auth with shared password support is deployed.
# Passwords remain in the upstream shared account store; OTP always remains.
COSIFT_SHARED_PASSWORD_ENABLED=0
# Shared mode requires ALL four values below; database never defaults silently.
COSIFT_SHARED_PROJECT=
COSIFT_SHARED_DATABASE=
Expand Down
20 changes: 13 additions & 7 deletions docs/AGENT-SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,23 +123,29 @@ uses server compute; local embeddings do not bypass validation.

## Allowance and credits

Web, CLI, and MCP searches share the account's gateway allowance and credit ledger.
Every account gets **60 shared free requests per minute plus 1,000 free credits
per UTC calendar month**, with no subscription required. The monthly grant is
Web, CLI, and MCP searches share the account's gateway limits and credit ledger.
Every account gets **1,000 free credits per UTC calendar month**, with no
subscription required. The monthly grant is
applied once on authenticated use in the current month; inactive past months are
not backfilled. Unused credits carry over.

A verified new contribution earns **10 credits** once per unique content.
Rejected, unverified, duplicate, or already-indexed pages earn none. After the
shared free requests, an extra successful request spends:
Rejected, unverified, duplicate, or already-indexed pages earn none. Every
successful authenticated retrieval spends credits, starting with the first request:

| Mode | Credits | Hard cap per account |
| Mode | Credits per successful request | Hard cap per account |
| --- | --- | --- |
| Search | 1 | 120/minute |
| Answer | 2 | 20/minute |
| Research | 3 | 3/10 minutes |

Failed backend requests release reservations and refund credits. Credits do not
There is no free per-minute member bypass. Credits are reserved before backend
work; insufficient credit stops the request. Failed backend requests release
reservations and refund credits. The monthly free grant, earned credits, and
purchases all fund the same balance. Guest retrieval has one shared cooldown per
public IP: successful Search waits 30 minutes, Answer 60, and Research 90. The
cooldown blocks every mode, so changing modes does not bypass it; failed backend
requests do not consume it. Web and CLI use the same guest policy. Credits do not
bypass mode limits or MCP's separate daily call cap. Check
[`/api/limits`](https://cosift.pilotprotocol.network/api/limits) and the authenticated
credits view for current policy. Respect retry guidance after a rate limit.
Expand Down
52 changes: 33 additions & 19 deletions docs/COMMUNITY-ROLLOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,33 @@ reuse the withdrawn v0.2.6 artifacts.

## Default quotas and public API compatibility

| Operation | Member hard cap | Guest hard cap |
| --- | --- | --- |
| Search | 120/minute | 1/minute |
| Answer | 20/minute | 1/5 minutes |
| Research | 3/10 minutes | 1/30 minutes |

Guests also share one request/minute across retrieval and contributions. Members
have 60 shared free requests/minute; one credit pays for each extra request
within the hard caps. Credit balance never bypasses a cap. Backend failures
release mode slots and refund guest allowances, free member reservations and
credits. Mode and shared free quotas persist across restarts. Search-only usage
can use 60 free requests and then 60 credit-funded requests/minute. Repeated CLI
commands should use `cosift login -session-file FILE` to avoid repeated password
logins and the separate authentication throttle.
| Operation | Credits per successful authenticated request | Member hard cap | Shared guest cooldown after success |
| --- | --- | --- | --- |
| Search | 1 | 120/minute | 30 minutes |
| Answer | 2 | 20/minute | 60 minutes |
| Research | 3 | 3/10 minutes | 90 minutes |

Every successful authenticated retrieval spends credits from its first request.
Each account receives 1,000 free credits per UTC calendar month; there is no
free per-minute member bypass. Unused credits carry over. The old
`-member-free-rpm` option is deprecated and ignored. Guests share one persistent
IP cooldown across all retrieval modes. The command's `-guest-interval` defaults
to `30m`; Search/Answer/Research multiply it by 1/2/3 after success. For example,
a guest Answer also blocks Search for 60 minutes. Changing modes does not bypass
the outstanding cooldown. Backend failures release guest reservations. Web and
CLI guests share this server policy. Contributions always require authentication.

Reserve credits and a mode slot atomically before dispatch. A failed backend
request must release its slot and refund its credit reservation; an insufficient
balance must prevent backend work. Credit balance never bypasses a hard cap, and
mode counters and the ledger persist across restarts. For rollout acceptance,
verify actual balance changes of -1/-2/-3 for a member's first Search/Answer/Research
request, and -1 for MCP search under that same account. A prior free-quota test is
not evidence that the new metering policy works. Repeat a failure and verify no
net debit; confirm the next UTC monthly grant happens only once per account.
Use the installer's private saved CLI session for repeated commands. Verify
guest cooldown duration for all three modes, cross-mode rejection, restart
persistence, and no consumption after failed backend work.

Public `/search`, `/answer` and `/research` now use the portal and accept GET
with `q`, matching the app/CLI. Existing public POST, streaming, or advanced
Expand Down Expand Up @@ -161,9 +174,10 @@ are not classified. Unreadable, oversized or uncertain pages remain unverified.
Obvious junk is screened before the model; the model handles broader spam and
content judgments. Local embeddings are checked against server computation,
so this first version does not promise server-compute savings. New content earns
10 credits, globally deduplicated by content hash. Stripe one-time credit purchases are implemented but disabled until the secret
API key and webhook signing secret are configured. See [Stripe activation and
10 credits, globally deduplicated by content hash. Stripe subscriptions and
subscriber-only top-ups require live billing configuration, verified webhooks,
and the dedicated restricted portal configuration. See [Stripe activation and
test-mode checks](STRIPE.md). Shared mode verifies email codes through
`cosift-auth`; standalone local mode still lacks email verification and
self-service password reset. Article authoring and rewards for article views
remain outside this release.
`cosift-auth`; optional password setup/reset requires a fresh email code.
Standalone local mode still lacks email verification and self-service password
reset. Article authoring and rewards for article views remain outside this release.
15 changes: 15 additions & 0 deletions docs/COMMUNITY-VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,18 @@ require internet access. JavaScript regressions are now included in PR CI.
receipt keys are included with the engine's Pebble data and must be retained
alongside community ledger backups. Upgrade the backend before the portal;
an older backend cannot provide durable reward receipts.


## Metering policy changed after this validation

The recorded free-per-minute member allowance is historical. Current policy
charges every successful authenticated Search/Answer/Research request 1/2/3
credits, including the first request, and grants each account 1,000 free credits
per UTC month. Guest limits also changed after the original validation: a
successful Search/Answer/Research starts one shared IP cooldown of 30/60/90
minutes, respectively; mode changes cannot bypass it. Prior tests that count
free member requests establish behavior before this change; they do not verify
current deductions. New rollout acceptance must check actual ledger debits from
the first request across web, CLI, and MCP search, no debit after backend failure,
and refusal before backend work when the balance is insufficient. See
[the current operator policy](COMMUNITY-ROLLOUT.md#default-quotas-and-public-api-compatibility).
63 changes: 38 additions & 25 deletions docs/COMMUNITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@ People can:
- Follow topics across the web and MCP, check article coverage, and record requests for missing articles. Automatic article authoring is still in development.
- View monthly credits and manage an optional paid subscription or subscriber top-up when live payments are configured.

Guests share **one successful Search, Research, or Answer request per minute per IP**.
Guest Answer is additionally capped at one per 5 minutes and Research at one per
30 minutes. Reading pages or checking the allowance is free. Invalid input and
failed backend requests do not consume the allowance. HTTP 429 includes
`Retry-After`, `retry_at`, and `retry_after_seconds`. People on a shared public IP
share this persistent, atomic guest allowance. **Contributions require login.**

Members receive **60 shared free requests per minute plus 1,000 free credits each
UTC calendar month**, 1,000 new contributed URLs per rolling 24 hours, and 200
Guests share one persistent cooldown per public IP. A successful **Search holds
it for 30 minutes, Answer for 60 minutes, or Research for 90 minutes**. The
cooldown applies across all three modes: after Answer, even Search must wait
60 minutes. Changing modes does not create another allowance. People sharing a
public IP also share this cooldown. Invalid input and failed backend requests
do not consume it; reading the app or checking the allowance is free. HTTP 429
includes `Retry-After`, `retry_at`, and `retry_after_seconds`.
**Contributions require login.**

Members receive **1,000 free credits each UTC calendar month**,
1,000 new contributed URLs per rolling 24 hours, and 200
saved searches. The monthly grant is applied once per account for the current
month on authenticated use; it does not accumulate grants for inactive past
months. All unused credits carry over. Mode caps and credit costs are described
below.
months. All unused credits carry over. Every successful authenticated Search,
Answer, or Research request spends credits, including the first request. There
is no free per-minute member bypass. Mode caps and credit costs are described below.

## Start the services

Expand Down Expand Up @@ -115,9 +118,9 @@ multiple rewards. Existing corpus URLs and rejected/unverified submissions do
not earn credits. A submission acknowledgement is not a reward: the backend must
confirm approved new content was indexed.

After the shared free 60 requests/minute, successful extra requests spend credits:
Every successful authenticated retrieval spends credits:

| Mode | Credits per extra request | Hard cap per account |
| Mode | Credits per successful request | Hard cap per account |
| --- | --- | --- |
| Search | 1 | 120/minute |
| Answer | 2 | 20/minute |
Expand All @@ -129,12 +132,18 @@ Credits cannot bypass hard caps. Mode limits persist across restarts and are
shared by sessions and public endpoint aliases. Failed backend requests release
reservations and refund debits. `GET /api/credits` returns the balance, current UTC
month's free/earned/purchased/spent activity, and policy. MCP search uses this same
gateway ledger; the MCP service also has a separate daily tool-call cap.
gateway ledger; the MCP service also has a separate daily tool-call cap. Credits
are reserved atomically before retrieval and retained only for a successful
backend response. Insufficient credit rejects the request before backend work;
contribute approved new content, add credits when billing is available, or wait
for the next monthly grant. Reading a balance or managing saved requests does
not itself spend retrieval credits.

## Plans and payments

The **Free plan requires no subscription** and includes the monthly 1,000 credits
and 60 free requests/minute. An optional **$5/month subscription adds 50,000 credits
The **Free plan requires no subscription** and includes the monthly 1,000 credits.
Those credits pay for requests at the same 1/2/3 rates. An optional
**$5/month subscription adds 50,000 credits
per paid month** and unlocks one-time **$5/50,000-credit top-ups**. Subscribers still
receive the free monthly credits. Unused credits carry over; cancellation does
not erase remaining earned or purchased credits. Refunds revoke the corresponding
Expand All @@ -153,13 +162,17 @@ through the same portal policy as `/api/*`. These public aliases support GET wit
portal. Unlisted native routes return 404 to prevent quota bypasses. The internal
loopback engine remains available to trusted operators.
`GET /api/limits` publishes current limits. Operators can configure
`-guest-interval`, `-member-free-rpm`, `-search-rpm`, `-answer-rpm`, and
`-research-per-10m` on the community command. A guest interval change preserves
the original request time instead of resetting all allowances. In-flight requests
reserve a mode slot; backend failures release it and refund charged credits.
The shared free member allowance also persists across restarts. Failed backend
requests release both free and mode reservations. At the defaults, Search-only
usage can consume 60 free requests and then 60 credit-funded requests per minute.
`-guest-interval`, `-search-rpm`, `-answer-rpm`, and `-research-per-10m` on
the community command. `-guest-interval` defaults to `30m`: successful Search,
Answer, and Research hold the shared guest cooldown for 1, 2, and 3 times that
base interval, respectively. The web app and CLI use the same server policy. `-member-free-rpm` is deprecated and ignored: it cannot
restore the old uncharged member allowance. A guest interval change preserves
the original request time instead of resetting allowances. In-flight requests
reserve a mode slot and, for authenticated retrieval, the mode's credit cost.
Failed backend requests release the mode reservation and refund credits. Mode
limits and balances persist across restarts. At the defaults, an account with
sufficient balance can make up to 120 Search requests/minute, spending one credit
for each successful response.

## CLI and CSV

Expand Down Expand Up @@ -241,14 +254,14 @@ enabled. CLI clients may omit Origin. Login returns an HttpOnly session cookie.
| `POST /api/logout` | Member | Revokes current session |
| `GET /api/me` | Member | Profile and interests |
| `PUT /api/interests` | Member | `{interests:[...]}`; completes onboarding, including an empty list |
| `GET /api/guest` | Public | Current IP's allowance and next available time |
| `GET /api/guest` | Public | Current IP's shared cooldown and next available time |
| `GET /api/search?q=...` | Guest or member | Cosift `/search`, preserving backend defaults |
| `GET /api/research?q=...` | Guest or member | Cosift `/research`; plan, synthesized answer and cited sources |
| `GET /api/answer?q=...` | Guest or member | Cosift `/answer`; direct answer and cited sources |
| `GET /api/saved` | Member | Own saved searches |
| `POST /api/saved` | Member | `{query,mode}`; mode defaults to `search`; idempotent per account/query/mode |
| `DELETE /api/saved/{id}` | Member | Removes an owned saved search |
| `GET /api/credits` | Member | Balance, monthly activity, weighted request costs, subscription state, top-up eligibility, and payment mode |
| `GET /api/credits` | Member | Balance, monthly activity, `all_authenticated_requests_metered:true`, weighted request costs, subscription state, top-up eligibility, and payment mode |
| `POST /api/payments/checkout` | Member | `{kind:"subscription"\|"topup",idempotency_key}`; returns a hosted Stripe Checkout URL |
| `POST /api/payments/portal` | Member | `{}`; returns an existing subscriber's restricted billing portal URL |
| `POST /api/payments/webhook` | Stripe signature | Paid invoice/top-up fulfillment, subscription state, and refund reconciliation |
Expand Down
15 changes: 15 additions & 0 deletions docs/PRODUCTION-READINESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,18 @@ a second full-corpus instance on the current production host.
Read-only inspection found the original engine active and the community service
and self-updater inactive. The configuration and on-disk rollback artifacts
remain separate from the reviewed candidate; this report authorizes no activation.


## Metering policy changed after this validation

The recorded free-per-minute member allowance is historical. Current policy
charges every successful authenticated Search/Answer/Research request 1/2/3
credits, including the first request, and grants each account 1,000 free credits
per UTC month. Guest limits also changed after the original validation: a
successful Search/Answer/Research starts one shared IP cooldown of 30/60/90
minutes, respectively; mode changes cannot bypass it. Prior tests that count
free member requests establish behavior before this change; they do not verify
current deductions. New rollout acceptance must check actual ledger debits from
the first request across web, CLI, and MCP search, no debit after backend failure,
and refusal before backend work when the balance is insufficient. See
[the current operator policy](COMMUNITY-ROLLOUT.md#default-quotas-and-public-api-compatibility).
Loading
Loading