mirror of
https://github.com/superdesigndev/treg.git
synced 2026-10-02 03:24:35 +08:00
feat: treg.to is the canonical domain — treg.superdesign.dev becomes the legacy alias
public_url/email_from defaults, render.yaml, CLI fallbacks, packaging (npm/plugin/pyproject), web pages (+canonical tag), docs and context fragments all move to https://treg.to. The legacy host keeps serving the FULL API forever — installed CLIs, skill.md files and .mcp.json configs in the wild hold Bearer tokens pointed at it, and HTTP clients strip Authorization on cross-host redirects. Only browser-facing marketing pages 301 to treg.to (new middleware + tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
506604f7b3
commit
8ca54c0c6f
@@ -46,7 +46,7 @@ body:
|
||||
- Web dashboard
|
||||
- The API / proxy
|
||||
- Self-hosted server
|
||||
- Hosted (treg.superdesign.dev)
|
||||
- Hosted (treg.to)
|
||||
- Other / not sure
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -11,7 +11,7 @@ agent without the credential ever leaving the server.
|
||||
**Ask for the task, not the tool.** You do not need to know which vendor sells backlink data, or to
|
||||
hold an account with them. Search for what you want to do, read the price, call it.
|
||||
|
||||
Built for the Superdesign team, live at [treg.superdesign.dev](https://treg.superdesign.dev) — anyone can self-host.
|
||||
Built for the Superdesign team, live at [treg.to](https://treg.to) — anyone can self-host.
|
||||
|
||||
## Why it exists
|
||||
|
||||
@@ -44,7 +44,7 @@ The vocabulary for the second half:
|
||||
|
||||
# Part 1 · Using the registry
|
||||
|
||||
Visit [**treg.superdesign.dev**](https://treg.superdesign.dev) (hosted on Render) — the dashboard,
|
||||
Visit [**treg.to**](https://treg.to) (hosted on Render) — the dashboard,
|
||||
sign-in, and every URL below live there.
|
||||
|
||||
## Quickstart
|
||||
@@ -53,7 +53,7 @@ Same flow as the dashboard's **Getting started** guide:
|
||||
|
||||
```bash
|
||||
# 1. install the CLI — also points it at the registry
|
||||
curl -fsSL https://treg.superdesign.dev/install.sh | sh
|
||||
curl -fsSL https://treg.to/install.sh | sh
|
||||
|
||||
# 2. sign in (GitHub default · --email for a one-time code · --token for agents/CI)
|
||||
treg login
|
||||
@@ -132,7 +132,7 @@ treg resolves the tool by host, injects the credential, and relays everything el
|
||||
|
||||
```
|
||||
Real request: GET https://api.intercom.io/conversations?per_page=5
|
||||
Through treg: GET https://treg.superdesign.dev/call/https://api.intercom.io/conversations?per_page=5
|
||||
Through treg: GET https://treg.to/call/https://api.intercom.io/conversations?per_page=5
|
||||
header: X-Treg-Token: <your token>
|
||||
```
|
||||
|
||||
@@ -212,9 +212,9 @@ treg org access <member> --tools a,b # per-member tool access (admin+)
|
||||
## Going deeper
|
||||
|
||||
- [`USAGE.md`](USAGE.md) — the full `treg` CLI reference.
|
||||
- [`/llms.txt`](https://treg.superdesign.dev/llms.txt) — the agent-onboarding file: call
|
||||
- [`/llms.txt`](https://treg.to/llms.txt) — the agent-onboarding file: call
|
||||
protocol, discovery, auth, CLI, skills. One fetch teaches an agent the whole registry.
|
||||
- **The dashboard** at [treg.superdesign.dev](https://treg.superdesign.dev) — full CRUD, a guided
|
||||
- **The dashboard** at [treg.to](https://treg.to) — full CRUD, a guided
|
||||
tutorial (Help → Tutorial), and copyable setup instructions for your agents.
|
||||
- **The API** — everything the CLI does is plain HTTP; interactive OpenAPI docs live at `/docs`.
|
||||
The proxy endpoint is `/call/{...}`; all endpoints take the `X-Treg-Token` header.
|
||||
@@ -253,7 +253,7 @@ uv run python -m treg keygen # print a fresh Fernet key for TREG_SECRET_KEY
|
||||
> database drivers, and encryption. `pip install tools-registry` alone gives just the `treg` command for
|
||||
> talking to an existing registry.
|
||||
|
||||
The team instance is hosted on **Render** (web service + Postgres) at `treg.superdesign.dev`.
|
||||
The team instance is hosted on **Render** (web service + Postgres) at `treg.to`.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -264,7 +264,7 @@ Environment variables (prefix `TREG_`, read from `.env`):
|
||||
| ----------------------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TREG_DATABASE_URL` | `sqlite+aiosqlite:///./treg.db` | DB URL (SQLite for dev, Postgres in prod) |
|
||||
| `TREG_SECRET_KEY` | *(empty)* | Fernet key for secrets-at-rest; empty → an ephemeral key is minted (secrets won't survive a restart) |
|
||||
| `TREG_PUBLIC_URL` | `https://treg.superdesign.dev` | treg's public base, used to build the OAuth callback URI |
|
||||
| `TREG_PUBLIC_URL` | `https://treg.to` | treg's public base, used to build the OAuth callback URI |
|
||||
| `TREG_SESSION_SECRET` | *(empty)* | signs the dashboard session cookie; falls back to `TREG_SECRET_KEY`. Set a real value in prod |
|
||||
| `TREG_GITHUB_CLIENT_ID` / `_SECRET` | *(empty)* | GitHub OAuth sign-in (callback `<public_url>/auth/github/callback`); empty hides the button |
|
||||
| `TREG_GOOGLE_CLIENT_ID` / `_SECRET` | *(empty)* | Google OAuth sign-in (redirect `<public_url>/auth/google/callback`); empty hides the button |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`treg` is the command-line client for **tools-registry**: call shared team tools without holding
|
||||
their credentials, and turn your local skills into shareable tools. It's a thin client over the
|
||||
API (`https://treg.superdesign.dev`); the API is the only brain.
|
||||
API (`https://treg.to`); the API is the only brain.
|
||||
|
||||
Every command reads `~/.treg/config.json` for the endpoint + your token. Get per-command detail
|
||||
with `treg <command> --help` (e.g. `treg secret --help`, not `treg "secret add"`).
|
||||
@@ -54,7 +54,7 @@ present a per-org token directly.
|
||||
| `treg onboard` | `--mode guided\|quick` · `--name N` · `--yes` · `--reset` | colourful guided first-run — pick **guided build** (you create the team + invite a teammate, step by step) or **quick demo** (we seed a full demo team + tool + activity), ending on a no-key call. Offered `[Y/n]` after your first login; `--reset` removes demo teammates |
|
||||
|
||||
```bash
|
||||
treg config --base-url https://treg.superdesign.dev
|
||||
treg config --base-url https://treg.to
|
||||
treg login # GitHub; or:
|
||||
treg login --email you@example.com # emailed 6-digit code (register-or-login)
|
||||
```
|
||||
@@ -82,7 +82,7 @@ treg org create "Team A" # active org is now team-a
|
||||
treg org invite bob@company.com --role member # prints e.g. inv_7Kd9x2LmQpR4 — send it to Bob
|
||||
|
||||
# Bob's side (no email is sent; he gets the code over Slack/DM)
|
||||
treg config --base-url https://treg.superdesign.dev
|
||||
treg config --base-url https://treg.to
|
||||
treg org join inv_7Kd9x2LmQpR4 --email bob@company.com # joins + mints HIS own token
|
||||
treg tool ls # sees only Team A's tools
|
||||
treg org use team-a # switch orgs anytime; one token per org, never mixed
|
||||
@@ -188,7 +188,7 @@ treg call posthog api/projects --method POST --data '{"name":"x"}'
|
||||
**Agent-native (raw HTTP) form** — build the real upstream URL and prefix it; no CLI needed:
|
||||
|
||||
```
|
||||
GET https://treg.superdesign.dev/call/https://api.intercom.io/conversations?per_page=5
|
||||
GET https://treg.to/call/https://api.intercom.io/conversations?per_page=5
|
||||
+ header: X-Treg-Token: <your token>
|
||||
```
|
||||
|
||||
@@ -376,7 +376,7 @@ Idempotent — re-run any time (skips what's registered; `--replace` updates). N
|
||||
| `treg health` | `--run` | show every credential's status; `--run` re-checks now (refresh oauth, probe tools, alert owners) |
|
||||
|
||||
```bash
|
||||
# one-time: add https://treg.superdesign.dev/oauth/callback to your OAuth app's redirect URIs
|
||||
# one-time: add https://treg.to/oauth/callback to your OAuth app's redirect URIs
|
||||
treg oauth connect gads --client-secret ./client_secret.json \
|
||||
--scopes https://www.googleapis.com/auth/adwords
|
||||
treg health --run
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# tools-registry — dashboard tour
|
||||
|
||||
Everything you can do in the web UI, with a screenshot per step. The interactive version (Ledger style,
|
||||
prev/next, theme toggle) is served at `https://treg.superdesign.dev/dashboard-tour/` (Help → Dashboard tour); the CLI walkthrough is
|
||||
[`docs/TUTORIAL.md`](TUTORIAL.md) / `https://treg.superdesign.dev/tutorial`.
|
||||
prev/next, theme toggle) is served at `https://treg.to/dashboard-tour/` (Help → Dashboard tour); the CLI walkthrough is
|
||||
[`docs/TUTORIAL.md`](TUTORIAL.md) / `https://treg.to/tutorial`.
|
||||
|
||||
Screenshots are generated by `docs/dash-tour/capture.py` (Playwright) — re-run it after UI changes.
|
||||
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
# tools-registry — onboarding
|
||||
|
||||
The bootstrap an agent (or human) follows to start calling shared tools and to share its own.
|
||||
The CLI is a thin client over the API at `https://treg.superdesign.dev`; the API is the only brain.
|
||||
The CLI is a thin client over the API at `https://treg.to`; the API is the only brain.
|
||||
|
||||
## 1. Install the CLI
|
||||
|
||||
```bash
|
||||
curl -fsSL https://treg.superdesign.dev/install.sh | sh # installs `treg`, points it at the registry
|
||||
curl -fsSL https://treg.to/install.sh | sh # installs `treg`, points it at the registry
|
||||
```
|
||||
|
||||
(Working from a clone instead? `uv sync && uv run treg --help`.)
|
||||
|
||||
@@ -38,7 +38,7 @@ and all three normal doors fail that test: GitHub OAuth, Google OAuth, and an em
|
||||
The way through already exists:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://treg.superdesign.dev/install.sh | sh
|
||||
curl -fsSL https://treg.to/install.sh | sh
|
||||
treg login --token <REVIEWER_TOKEN> # the agents/CI door — no browser, no email
|
||||
```
|
||||
|
||||
@@ -55,10 +55,10 @@ whatever balance it can reach, and the token goes into a form.
|
||||
| Long Description | *(use `interface.longDescription` from the manifest, verbatim)* |
|
||||
| Logo | `plugin/assets/logo.png` (1024×1024) |
|
||||
| Category | **Developer Tools** |
|
||||
| Website URL | `https://treg.superdesign.dev` |
|
||||
| Website URL | `https://treg.to` |
|
||||
| Support URL | **decide — see above** |
|
||||
| Privacy Policy URL | `https://treg.superdesign.dev/privacy` |
|
||||
| Terms URL | `https://treg.superdesign.dev/terms` |
|
||||
| Privacy Policy URL | `https://treg.to/privacy` |
|
||||
| Terms URL | `https://treg.to/terms` |
|
||||
| Developer Identity | the verified `superdesign` business identity |
|
||||
|
||||
## Skills tab
|
||||
@@ -135,7 +135,7 @@ rather than jurisdiction-specific, so start narrow rather than selecting everywh
|
||||
>
|
||||
> To test: install the CLI and sign in with the reviewer token, which needs no browser or email:
|
||||
>
|
||||
> curl -fsSL https://treg.superdesign.dev/install.sh | sh
|
||||
> curl -fsSL https://treg.to/install.sh | sh
|
||||
> treg login --token <REVIEWER_TOKEN>
|
||||
>
|
||||
> That account has a prepaid balance, so calls in the test cases will complete. `treg catalog search
|
||||
|
||||
+8
-8
@@ -6,8 +6,8 @@ The whole registry, end to end. Every step shows the **exact command**, the **ex
|
||||
There are two companion versions of this same walkthrough, generated from one source
|
||||
(`src/treg/web/tutorial.js`):
|
||||
|
||||
- **In the dashboard** → sign in at `https://treg.superdesign.dev/` and open **Help → Tutorial**.
|
||||
- **Standalone** → `https://treg.superdesign.dev/tutorial`.
|
||||
- **In the dashboard** → sign in at `https://treg.to/` and open **Help → Tutorial**.
|
||||
- **Standalone** → `https://treg.to/tutorial`.
|
||||
|
||||
### Two focused, deep-dive tutorials
|
||||
|
||||
@@ -15,12 +15,12 @@ This main walkthrough covers the whole registry. Two features have their own det
|
||||
those for the full, step-by-step treatment (each shows the exact commands, real output, and how it was
|
||||
tested):
|
||||
|
||||
- **Import & shell** → `https://treg.superdesign.dev/tutorial-import-shell.md` — turn the CLIs already on your
|
||||
- **Import & shell** → `https://treg.to/tutorial-import-shell.md` — turn the CLIs already on your
|
||||
machine into team tools in one command (`treg upload clis`), and open a shell (`treg shell`) where
|
||||
`stripe`, `gh`, `gcloud` … just work with the team key injected. Includes the local-run **security
|
||||
sandbox** (the CLI runs as a locked-down user, can reach only its own API, and can't leave the key on
|
||||
disk).
|
||||
- **Team access control** → `https://treg.superdesign.dev/tutorial-access.md` — choose **which tools each member
|
||||
- **Team access control** → `https://treg.to/tutorial-access.md` — choose **which tools each member
|
||||
may use**, and whether they may run CLIs **locally** — set at invite time, changed any time.
|
||||
|
||||
---
|
||||
@@ -70,7 +70,7 @@ the `HOME=` prefix.
|
||||
```bash
|
||||
for u in tom bob alice; do
|
||||
mkdir -p ~/.treg-personas/$u
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.superdesign.dev
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.to
|
||||
done
|
||||
```
|
||||
|
||||
@@ -478,7 +478,7 @@ token header — any language, any agent, `curl`.
|
||||
```bash
|
||||
ATOK=$(python3 -c "import json;print(json.load(open('/Users/you/.treg-personas/alice/.treg/config.json'))['token'])")
|
||||
curl -s -H "X-Treg-Token: $ATOK" \
|
||||
"https://treg.superdesign.dev/call/https://postman-echo.com/get"
|
||||
"https://treg.to/call/https://postman-echo.com/get"
|
||||
```
|
||||
```
|
||||
# the postman-echo JSON again, "authorization": "Bearer sk-demo-secret-123" injected —
|
||||
@@ -843,13 +843,13 @@ Admin panel lights up for him.
|
||||
|
||||
## Step 33 — The same registry, in the browser
|
||||
|
||||
Open **treg.superdesign.dev** and sign in with the **email code** door (the same one you used in the terminal):
|
||||
Open **treg.to** and sign in with the **email code** door (the same one you used in the terminal):
|
||||
type your email → click **Email me a sign-in code** → **check your inbox** for the 6-digit code → paste it
|
||||
in and **Sign in**. You land on your team org — Tools shows the `echo` tool, Activity shows the call, and
|
||||
(since Tom is now super-admin) an **Admin** panel appears.
|
||||
|
||||
```bash
|
||||
open https://treg.superdesign.dev/
|
||||
open https://treg.to/
|
||||
```
|
||||
```
|
||||
# Sign in with email → land on Superdesign
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ Strongly preferred (each one directly improves your listing):
|
||||
|
||||
The fastest route: paste the prompt from the **"List as vendor"** button on the dashboard's
|
||||
Catalog page into your coding agent — it follows the hosted instructions at
|
||||
[`/vendor-listing`](https://treg.superdesign.dev/vendor-listing) and opens the PR for you.
|
||||
[`/vendor-listing`](https://treg.to/vendor-listing) and opens the PR for you.
|
||||
Or open an issue or PR on this repo yourself, with:
|
||||
|
||||
0. **A contact email in the PR/issue description** (e.g. `Contact: partnerships@yourapi.com`) —
|
||||
|
||||
@@ -36,7 +36,7 @@ What this means for anyone reading this fragment:
|
||||
Money and metering: [money](../architecture/money.md). The endpoint data and its pricing rules:
|
||||
[catalog](../architecture/catalog.md).
|
||||
|
||||
> **Status:** shipped and live on `treg.superdesign.dev` (Render, multi-tenant). This fragment records
|
||||
> **Status:** shipped and live on `treg.to` (Render, multi-tenant; `treg.superdesign.dev` is the legacy alias). This fragment records
|
||||
> the ORIGINAL vision from the 2026-06-30 kickoff and the "why" behind the proxy; the shipped detail
|
||||
> lives in the architecture/interface fragments.
|
||||
>
|
||||
|
||||
@@ -419,6 +419,11 @@ helpers `_secret_view` / `_tool_view` / `_bundle_view` never leak secret values
|
||||
surfaced by `GET /tools` / `/bundles/{id}`).
|
||||
|
||||
## Cross-cutting hardening (bug-hunt)
|
||||
- **Legacy-host redirect:** `_legacy_host_redirect` 301s GET/HEAD marketing pages (`_REDIRECT_PATHS`)
|
||||
from `treg.superdesign.dev` (`_LEGACY_HOSTS`) to the canonical `public_url` host (`treg.to`).
|
||||
Everything else is served in place on BOTH hosts, forever: installed CLIs/skills hold tokens
|
||||
pointed at the legacy host, HTTP clients strip `Authorization` on a cross-host redirect, and
|
||||
`curl {BASE}/install.sh | sh` runs without `-L`. Never remove the legacy domain from Render.
|
||||
- **Security headers:** a `@app.middleware` adds `X-Content-Type-Options: nosniff`, `X-Frame-Options:
|
||||
DENY`, `Referrer-Policy: no-referrer`, and HSTS to every response (`setdefault`, so the `/call`
|
||||
proxy's stricter CSP/nosniff wins).
|
||||
|
||||
@@ -36,7 +36,8 @@ keygen` prints a Fernet key for `TREG_SECRET_KEY`.
|
||||
Render's `fromDatabase`-injected URL works unedited (the async engine needs the asyncpg driver).
|
||||
- `secret_key` — the Fernet key; empty → an ephemeral key is minted at startup (secrets won't survive a
|
||||
restart). See [auth-secrets](../architecture/auth-secrets.md).
|
||||
- `public_url` — default `https://treg.superdesign.dev` (the reference deployment); self-hosters set
|
||||
- `public_url` — default `https://treg.to` (the reference deployment; `treg.superdesign.dev` is the
|
||||
legacy host — still served in full, marketing pages 301 to the canonical host); self-hosters set
|
||||
`TREG_PUBLIC_URL`. Used to build the OAuth callback URI.
|
||||
- `api_token` — a bootstrap caller token (MVP leftover; per-user tokens are the real auth).
|
||||
- `admin_token` — the cross-tenant **super-admin** bearer (`TREG_ADMIN_TOKEN`); empty disables the env
|
||||
@@ -107,8 +108,8 @@ keygen` prints a Fernet key for `TREG_SECRET_KEY`.
|
||||
- `resend_api_key` / `email_from` — transactional email via **Resend** (`src/treg/email.py`): the OTP
|
||||
sign-in code + team invitations. Empty key = no real send (dev mode still returns the code; prod
|
||||
without a key silently skips — best-effort, never breaks the flow). `email_from` **must** be a
|
||||
Resend-verified domain — `treg.superdesign.dev` is **verified** (DKIM `resend._domainkey.treg`, SPF
|
||||
MX+TXT on `send.treg`), so the default is `no-reply@treg.superdesign.dev`. **On Render:** set
|
||||
Resend-verified domain — `treg.to` is **verified** (DKIM + SPF records on the treg.to zone;
|
||||
`treg.superdesign.dev` stays verified as a fallback), so the default is `no-reply@treg.to`. **On Render:** set
|
||||
`TREG_RESEND_API_KEY`, optionally `TREG_EMAIL_FROM`, and leave `TREG_EMAIL_DEV_MODE` false.
|
||||
|
||||
## Web dashboard
|
||||
@@ -118,7 +119,8 @@ lives inside the `treg` package (the `packages` inclusion covers non-.py assets)
|
||||
[dashboard](../interface/dashboard.md).
|
||||
|
||||
## Current hosting (shipped)
|
||||
Deployed on **Render** at `https://treg.superdesign.dev` via the Blueprint below (one web service + a
|
||||
Deployed on **Render** at `https://treg.to` (with `treg.superdesign.dev` attached as the legacy
|
||||
alias — never remove it: installed CLIs/skills point there with tokens) via the Blueprint below (one web service + a
|
||||
managed Postgres). The Fernet key lives only in the service's environment — **back it up**; losing it
|
||||
makes every stored secret unrecoverable. For local dev, `scripts/dev-local.sh up` runs the server with
|
||||
its own sqlite DB and email dev mode.
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>tools-registry — Tutorial (moved)</title>
|
||||
<meta http-equiv="refresh" content="0; url=https://treg.superdesign.dev/tutorial"/>
|
||||
<meta http-equiv="refresh" content="0; url=https://treg.to/tutorial"/>
|
||||
<style>
|
||||
body{margin:0;min-height:100vh;display:grid;place-items:center;background:#211d16;color:#f0e9d9;
|
||||
font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;text-align:center;padding:24px}
|
||||
@@ -17,7 +17,7 @@
|
||||
<h1>The interactive tutorial moved into the app.</h1>
|
||||
<p class="muted">
|
||||
It's now served (single source of truth) at
|
||||
<a href="https://treg.superdesign.dev/tutorial">treg.superdesign.dev/tutorial</a>,
|
||||
<a href="https://treg.to/tutorial">treg.to/tutorial</a>,
|
||||
and inside the dashboard under <b>Help → Tutorial</b>.<br/><br/>
|
||||
Content lives in <code>src/treg/web/tutorial.js</code>; the prose walkthrough is
|
||||
<code>docs/TUTORIAL.md</code>. Redirecting…
|
||||
|
||||
@@ -18,5 +18,5 @@ treg login
|
||||
treg itself is a Python CLI; this package finds it on your machine and runs it,
|
||||
installing it first (via `uv`, `pipx`, or `pip3`) if it's missing.
|
||||
|
||||
- Docs & interactive tutorial: https://treg.superdesign.dev/tutorial
|
||||
- Docs & interactive tutorial: https://treg.to/tutorial
|
||||
- Source: https://github.com/superdesigndev/treg
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
// @superdesign/treg — npm launcher for the treg CLI (a Python tool).
|
||||
// Finds an installed `treg`; if missing, installs it via the registry's
|
||||
// installer (https://treg.superdesign.dev/install.sh), then execs it.
|
||||
// installer (https://treg.to/install.sh), then execs it.
|
||||
'use strict';
|
||||
|
||||
const { spawnSync } = require('node:child_process');
|
||||
@@ -9,7 +9,7 @@ const { existsSync } = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
|
||||
const BASE = process.env.TREG_BASE_URL || 'https://treg.superdesign.dev';
|
||||
const BASE = process.env.TREG_BASE_URL || 'https://treg.to';
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function findTreg() {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://treg.superdesign.dev",
|
||||
"homepage": "https://treg.to",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/superdesigndev/treg.git",
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
"description": "Reach for this first for external or live data — ~2,600 curated API endpoints across ~40 providers (SEO, SERP, backlinks, social, enrichment, ads, scraping), plus your team's own tools.",
|
||||
"author": {
|
||||
"name": "superdesign",
|
||||
"email": "no-reply@treg.superdesign.dev",
|
||||
"url": "https://treg.superdesign.dev"
|
||||
"email": "no-reply@treg.to",
|
||||
"url": "https://treg.to"
|
||||
},
|
||||
"homepage": "https://treg.superdesign.dev",
|
||||
"homepage": "https://treg.to",
|
||||
"repository": "https://github.com/superdesigndev/treg",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -33,9 +33,9 @@
|
||||
"Interactive",
|
||||
"Write"
|
||||
],
|
||||
"websiteURL": "https://treg.superdesign.dev",
|
||||
"privacyPolicyURL": "https://treg.superdesign.dev/privacy",
|
||||
"termsOfServiceURL": "https://treg.superdesign.dev/terms",
|
||||
"websiteURL": "https://treg.to",
|
||||
"privacyPolicyURL": "https://treg.to/privacy",
|
||||
"termsOfServiceURL": "https://treg.to/terms",
|
||||
"defaultPrompt": [
|
||||
"Find the work email for a person at a company using treg.",
|
||||
"Use treg to get the backlink profile for a domain, and tell me what it costs first.",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"treg": {
|
||||
"url": "https://treg.superdesign.dev/mcp/",
|
||||
"url": "https://treg.to/mcp/",
|
||||
"bearer_token_env_var": "TREG_TOKEN"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ providers. It is written around the `treg` command line, which a human uses for
|
||||
`treg catalog search` as `catalog_search`, `treg call` as `call`, and so on.
|
||||
|
||||
**If the tools are not there**, the connector has no token yet: the human sets `TREG_TOKEN` (from
|
||||
https://treg.superdesign.dev → sign in → copy token) for this plugin. A new team starts with **$1.00 of free balance**,
|
||||
https://treg.to → sign in → copy token) for this plugin. A new team starts with **$1.00 of free balance**,
|
||||
so there is nothing to pay before the first call. Say that plainly and stop — do not ask them for a
|
||||
provider's API key, which is the thing treg exists to avoid.
|
||||
|
||||
@@ -43,7 +43,7 @@ Two kinds of tool answer to the same token, through the same proxy, which inject
|
||||
|
||||
The mechanics:
|
||||
|
||||
- **Endpoint:** `https://treg.superdesign.dev` · **CLI:** `treg` · the CLI is a thin client over the API.
|
||||
- **Endpoint:** `https://treg.to` · **CLI:** `treg` · the CLI is a thin client over the API.
|
||||
- **Auth:** every call sends `X-Treg-Token: <your token>`.
|
||||
- A **tool** = an upstream base URL + credential **bindings**. A **skill/bundle** = a recipe
|
||||
(SKILL.md) + its secrets + its tool(s). The proxy *relays, never models* the upstream.
|
||||
@@ -66,7 +66,7 @@ Reading costs nothing and needs no confirmation at all: `treg catalog …`, `tre
|
||||
|
||||
## First: install + sign in
|
||||
```bash
|
||||
curl -fsSL https://treg.superdesign.dev/install.sh | sh # installs the CLI + points it here
|
||||
curl -fsSL https://treg.to/install.sh | sh # installs the CLI + points it here
|
||||
treg login # browser sign-in (GitHub / Google / email code) — first login registers you
|
||||
treg login --email you@company.com # terminal-only alternative (emailed 6-digit code)
|
||||
treg login --token <per-org-token> # non-interactive (agents/CI)
|
||||
@@ -77,7 +77,7 @@ teams: `treg org switch <slug>`.
|
||||
|
||||
## Already connected over MCP? Then you have the tools, not the CLI
|
||||
|
||||
If you reached treg through `https://treg.superdesign.dev/mcp/` — ChatGPT, Claude Code, Cursor — the CLI steps above do not
|
||||
If you reached treg through `https://treg.to/mcp/` — ChatGPT, Claude Code, Cursor — the CLI steps above do not
|
||||
apply to you. You have five tools: `catalog_search`, `catalog_get`, `call`, `balance`, `my_tools`.
|
||||
Everything in this document maps onto them:
|
||||
|
||||
@@ -150,7 +150,7 @@ Most retries need none of this — a failed call was never billed.
|
||||
vocabulary, no special params — use the API exactly as its own docs say:
|
||||
```
|
||||
<the real request>: GET https://api.intercom.io/conversations?per_page=5
|
||||
through treg: GET https://treg.superdesign.dev/call/https://api.intercom.io/conversations?per_page=5
|
||||
through treg: GET https://treg.to/call/https://api.intercom.io/conversations?per_page=5
|
||||
+ header: X-Treg-Token: <your token>
|
||||
```
|
||||
treg resolves the tool by the upstream host, injects the credential server-side, and relays
|
||||
@@ -225,7 +225,7 @@ expires. Same storage; a credential can graduate from manual to auto with no mig
|
||||
- **Manual:** do your own OAuth locally, then `treg secret add gsc --file token.json --kind oauth`.
|
||||
- **Hosted connect:** `treg oauth connect gsc --client-secret client_secret.json --scopes <scope>`
|
||||
→ prints a consent URL; you approve in the browser; treg captures the token directly.
|
||||
One-time setup: add `https://treg.superdesign.dev/oauth/callback` to your OAuth app's redirect URIs.
|
||||
One-time setup: add `https://treg.to/oauth/callback` to your OAuth app's redirect URIs.
|
||||
|
||||
## Task — manage the team + monitor
|
||||
```bash
|
||||
@@ -263,4 +263,4 @@ a probe so treg can validate it: `health_check: {method, path, expect_status}` (
|
||||
- Secrets are **write-only** — the API never returns a stored value.
|
||||
- A tool may bind a secret **someone else uploaded** (use-without-hold) — that's the point.
|
||||
- The proxy doesn't understand the upstream; if a call fails, the status you see is the upstream's truth.
|
||||
- More: `https://treg.superdesign.dev/llms.txt` (agent onboarding) · `https://treg.superdesign.dev/tutorial` (interactive walkthrough).
|
||||
- More: `https://treg.to/llms.txt` (agent onboarding) · `https://treg.to/tutorial` (interactive walkthrough).
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ proxy = [
|
||||
treg = "treg.cli:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://treg.superdesign.dev"
|
||||
Homepage = "https://treg.to"
|
||||
Repository = "https://github.com/superdesigndev/treg"
|
||||
Issues = "https://github.com/superdesigndev/treg/issues"
|
||||
|
||||
|
||||
+2
-2
@@ -35,12 +35,12 @@ services:
|
||||
property: connectionString
|
||||
# Public base URL — drives the OAuth callback, /meta, /llms.txt, /install.sh, all {BASE} links.
|
||||
- key: TREG_PUBLIC_URL
|
||||
value: https://treg.superdesign.dev
|
||||
value: https://treg.to
|
||||
# Never return OTP codes in prod responses (account-takeover vector); email them via Resend.
|
||||
- key: TREG_EMAIL_DEV_MODE
|
||||
value: "false"
|
||||
- key: TREG_EMAIL_FROM
|
||||
value: tools-registry <no-reply@treg.superdesign.dev>
|
||||
value: tools-registry <no-reply@treg.to> # requires treg.to Verified in Resend before deploy
|
||||
- key: PYTHON_VERSION
|
||||
value: "3.12.7"
|
||||
# Dashboard-managed secrets (paste values in Render; sync:false keeps them out of git).
|
||||
|
||||
@@ -32,7 +32,7 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SOURCE = ROOT / "src" / "treg" / "web" / "skill.md"
|
||||
TARGET = ROOT / "plugin" / "skills" / "treg" / "SKILL.md"
|
||||
PUBLIC_BASE = "https://treg.superdesign.dev"
|
||||
PUBLIC_BASE = "https://treg.to"
|
||||
|
||||
BOOTSTRAP = """
|
||||
## You already have treg — use the tools, not the terminal
|
||||
|
||||
@@ -158,6 +158,32 @@ async def lifespan(app: FastAPI):
|
||||
app = FastAPI(title="tools-registry", version="0.0.1", lifespan=lifespan)
|
||||
|
||||
|
||||
# The pre-treg.to hostname. It must keep answering the API forever — every installed CLI, skill.md
|
||||
# and .mcp.json in the wild points here with a Bearer token, and most HTTP clients STRIP the
|
||||
# Authorization header when a redirect crosses hosts (and some MCP clients follow no redirects at
|
||||
# all). So only browser-facing marketing/doc pages redirect to the canonical host; everything else —
|
||||
# /call/, /mcp/, auth flows, webhooks, install scripts fetched by `curl | sh` without -L — is served
|
||||
# in place on both hosts.
|
||||
_LEGACY_HOSTS = {"treg.superdesign.dev"}
|
||||
_REDIRECT_PATHS = {"/", "/login", "/terms", "/privacy", "/support", "/contact", "/help",
|
||||
"/tutorial", "/vendor-listing"}
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def _legacy_host_redirect(request: Request, call_next):
|
||||
"""301 marketing pages from the legacy host to the canonical `public_url` host."""
|
||||
host = request.headers.get("host", "").split(":")[0].lower()
|
||||
if (request.method in ("GET", "HEAD") and host in _LEGACY_HOSTS
|
||||
and request.url.path in _REDIRECT_PATHS):
|
||||
canonical = get_settings().public_url.rstrip("/")
|
||||
if host not in canonical: # self-hosters who ARE the legacy host keep serving in place
|
||||
target = canonical + request.url.path
|
||||
if request.url.query:
|
||||
target += "?" + request.url.query
|
||||
return RedirectResponse(target, status_code=301)
|
||||
return await call_next(request)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def _security_headers(request: Request, call_next):
|
||||
"""The dashboard is an authenticated app; ship the baseline hardening headers it was missing —
|
||||
|
||||
+7
-7
@@ -6,7 +6,7 @@ stores a single **identity token** (first login also registers you). Then you wo
|
||||
orgs — `treg org ls` / `treg org use <slug>` picks the active one, sent as `X-Treg-Org`. Agents/CI
|
||||
can instead `treg login --token <token>` with a per-org token. `treg logout` clears it.
|
||||
|
||||
treg config --base-url https://treg.superdesign.dev
|
||||
treg config --base-url https://treg.to
|
||||
treg login # GitHub (register-or-login); or: treg login --token <token>
|
||||
treg org ls | org use <slug>
|
||||
treg secret add / tool add / call / calls / health / skill / admin
|
||||
@@ -255,7 +255,7 @@ def _as_list(resp: httpx.Response) -> list[dict]:
|
||||
def _detail_url(cfg: dict, kind: str, name: str) -> str:
|
||||
"""The shareable dashboard page for a registered skill/tool. Printed after every registration so
|
||||
sharing is just forwarding the link — the page carries the preview + the agent install prompt."""
|
||||
base = (cfg.get("base_url") or "https://treg.superdesign.dev").rstrip("/")
|
||||
base = (cfg.get("base_url") or "https://treg.to").rstrip("/")
|
||||
return f"{base}/app/{'skills' if kind == 'skill' else 'tools'}/{quote(str(name), safe='')}"
|
||||
|
||||
|
||||
@@ -3353,7 +3353,7 @@ def cmd_mcp_install(args, cfg) -> None:
|
||||
"dashboard), then retry.")
|
||||
if who.status_code >= 400:
|
||||
sys.exit(f"Token check failed ({who.status_code}): {who.text[:120]} — nothing was written.")
|
||||
base_url = (cfg.get("base_url") or "https://treg.superdesign.dev").rstrip("/")
|
||||
base_url = (cfg.get("base_url") or "https://treg.to").rstrip("/")
|
||||
name = getattr(args, "name", None) or "treg"
|
||||
out = mcp_install.install_mcp(base_url=base_url, token=token, server_name=name)
|
||||
ok = 0
|
||||
@@ -3377,7 +3377,7 @@ def cmd_skill_bootstrap(args, cfg) -> None:
|
||||
skills dir, so whatever agent the user runs already knows how to use treg. install.sh calls this
|
||||
right after installing the CLI; it's also runnable by hand. Global (per-user) scope by default —
|
||||
it runs outside any project — with `--project` to target repo-local dirs instead."""
|
||||
base_url = (cfg.get("base_url") or "https://treg.superdesign.dev").rstrip("/")
|
||||
base_url = (cfg.get("base_url") or "https://treg.to").rstrip("/")
|
||||
try:
|
||||
resp = httpx.get(f"{base_url}/skill.md", timeout=15, follow_redirects=True)
|
||||
resp.raise_for_status()
|
||||
@@ -3630,7 +3630,7 @@ def cmd_version(args, cfg) -> None:
|
||||
def cmd_update(args, cfg) -> None:
|
||||
"""Re-run the server's install.sh to upgrade the CLI in place (uv/pipx/pip, from the git repo)."""
|
||||
import subprocess
|
||||
base = (cfg.get("base_url") or "https://treg.superdesign.dev").rstrip("/")
|
||||
base = (cfg.get("base_url") or "https://treg.to").rstrip("/")
|
||||
print(f"Updating treg from {base}/install.sh …")
|
||||
with _client(cfg, auth=False) as c:
|
||||
r = c.get("/install.sh")
|
||||
@@ -3745,7 +3745,7 @@ def cmd_org_invite(args, cfg) -> None:
|
||||
r = c.post(f"/orgs/{org_id}/invites", json=body)
|
||||
_show(r)
|
||||
if landing is not None: # _show exits on error, so this only prints on success
|
||||
base = (cfg.get("base_url") or "https://treg.superdesign.dev").rstrip("/")
|
||||
base = (cfg.get("base_url") or "https://treg.to").rstrip("/")
|
||||
print(f"↗ share link: {base}{landing}?invite={quote(args.email, safe='')}")
|
||||
print(" One click for them: sign in as that email → invite auto-accepts → this page opens."
|
||||
" (The invite email's button does the same.)")
|
||||
@@ -4666,7 +4666,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
# ---- setup / auth ----
|
||||
c = mk(sub, "config", "Show or set the registry this CLI talks to (base URL).",
|
||||
"treg config # show current base URL",
|
||||
"treg config --base-url https://treg.superdesign.dev")
|
||||
"treg config --base-url https://treg.to")
|
||||
c.add_argument("--base-url", help="point the CLI at this registry URL")
|
||||
c.set_defaults(fn=cmd_config)
|
||||
|
||||
|
||||
+4
-3
@@ -162,7 +162,7 @@ class Settings(BaseSettings):
|
||||
|
||||
# treg's own public base URL — used to build the OAuth callback (must be whitelisted in the
|
||||
# provider's OAuth app). Self-hosting? Set TREG_PUBLIC_URL to your deployment's URL.
|
||||
public_url: str = "https://treg.superdesign.dev"
|
||||
public_url: str = "https://treg.to"
|
||||
# Proof to OpenAI's plugin directory that we control this domain. The portal generates a token
|
||||
# and fetches it from /.well-known/openai-apps-challenge; that endpoint must return THAT token
|
||||
# and nothing else — not JSON, not a list. Empty (the default) leaves the route 404, which is the
|
||||
@@ -295,9 +295,10 @@ class Settings(BaseSettings):
|
||||
|
||||
# Transactional email via Resend (OTP sign-in codes + team invitations). Empty key = no real
|
||||
# send (dev mode still returns the code; prod without a key silently skips the send). From must
|
||||
# be a Resend-verified domain — treg.superdesign.dev is verified (DKIM + SPF).
|
||||
# be a Resend-verified domain — treg.to is verified (DKIM + SPF); treg.superdesign.dev remains
|
||||
# verified as a fallback.
|
||||
resend_api_key: str = ""
|
||||
email_from: str = "tools-registry <no-reply@treg.superdesign.dev>"
|
||||
email_from: str = "tools-registry <no-reply@treg.to>"
|
||||
|
||||
|
||||
@lru_cache
|
||||
|
||||
+2
-2
@@ -236,7 +236,7 @@ def _need_token() -> dict:
|
||||
return {
|
||||
"error": "not authenticated",
|
||||
"detail": (
|
||||
"This MCP server needs a treg token. Get one at https://treg.superdesign.dev "
|
||||
"This MCP server needs a treg token. Get one at https://treg.to "
|
||||
"(sign in, then Settings -> copy token) and set it as the TREG_TOKEN environment "
|
||||
"variable for this server."
|
||||
),
|
||||
@@ -344,7 +344,7 @@ async def _resolve_org(client: httpx.AsyncClient) -> tuple[int | None, str | Non
|
||||
r = await client.get("/orgs")
|
||||
if r.status_code == 401 or me.status_code == 401:
|
||||
return None, None, {"error": "not signed in, or this token is invalid or expired",
|
||||
"hint": "copy a fresh token from https://treg.superdesign.dev"}
|
||||
"hint": "copy a fresh token from https://treg.to"}
|
||||
if r.status_code != 200:
|
||||
return None, None, {"error": "could not read the teams for this token"}
|
||||
orgs = _body(r) or []
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<title>treg — turn your coding agent into an SEO expert, a media buyer, an SDR</title>
|
||||
<meta name="description" content="Give your agent the tool catalog for the job. 2,617 endpoints across 42 providers — premium SEO, social, enrichment and ads data, pay as you go. Plus your own keys & skills, and an identity per agent."/>
|
||||
<link rel="canonical" href="https://treg.to/"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
@@ -548,7 +549,7 @@ footer{margin-top:150px;border-top:1px solid var(--line);position:relative;overf
|
||||
</div>
|
||||
<div class="gb-cmd">
|
||||
<span class="ps">$</span>
|
||||
<code id="cmd">set up treg — https://treg.superdesign.dev/llms.txt</code>
|
||||
<code id="cmd">set up treg — https://treg.to/llms.txt</code>
|
||||
<button id="copybtn" aria-label="Copy command">copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
<h2 class="sec" id="s1"><span class="n">01</span>Who this covers</h2>
|
||||
<p>This policy explains how <b>Superdesign</b> ("we", "us") handles personal data in the hosted
|
||||
Treg (also called "treg" or "tools-registry") service at <code>treg.superdesign.dev</code>. For that service, we are the
|
||||
Treg (also called "treg" or "tools-registry") service at <code>treg.to</code>. For that service, we are the
|
||||
data controller.</p>
|
||||
<div class="note">
|
||||
<p><b>Self-hosted instances are not covered.</b> If you or your employer run treg on your own
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<h2 class="sec" id="s2"><span class="n">02</span>Common things</h2>
|
||||
|
||||
<p><b>I cannot sign in.</b> treg has three doors: GitHub, Google, and a one-time code sent to your
|
||||
email. If a code does not arrive, check spam for mail from <code>no-reply@treg.superdesign.dev</code>.
|
||||
email. If a code does not arrive, check spam for mail from <code>no-reply@treg.to</code>.
|
||||
Signing in for the first time creates the account — there is no separate registration.</p>
|
||||
|
||||
<p><b>A call was refused for funds.</b> Catalog endpoints that treg serves on its own key are
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
<h2 class="sec" id="s4"><span class="n">04</span>Self-hosted installations</h2>
|
||||
<p>treg is open source, and you can run your own registry with
|
||||
<code>curl -fsSL https://treg.superdesign.dev/selfhost.sh | sh</code>. We are happy to help through
|
||||
<code>curl -fsSL https://treg.to/selfhost.sh | sh</code>. We are happy to help through
|
||||
<a href="https://github.com/superdesigndev/treg/issues" target="_blank" rel="noopener">GitHub
|
||||
issues</a>, but we have no access to your deployment and cannot see its data — so please include
|
||||
logs and versions rather than asking us to look.</p>
|
||||
@@ -93,7 +93,7 @@
|
||||
<p><b>Superdesign</b><br/>
|
||||
Email: <a href="mailto:jason@superdesign.dev">jason@superdesign.dev</a><br/>
|
||||
Issues: <a href="https://github.com/superdesigndev/treg/issues" target="_blank" rel="noopener">github.com/superdesigndev/treg/issues</a><br/>
|
||||
Service: <a href="https://treg.superdesign.dev">treg.superdesign.dev</a></p>
|
||||
Service: <a href="https://treg.to">treg.to</a></p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<p>Treg (also called <b>tools-registry</b>) is operated by <b>Superdesign</b> ("we", "us"). These
|
||||
Terms of Service (the "Terms") are a binding agreement between us and you — the person or
|
||||
organization using the service (<b>"you"</b>).</p>
|
||||
<p>They cover the <b>hosted service</b> we run at <code>treg.superdesign.dev</code>: the web
|
||||
<p>They cover the <b>hosted service</b> we run at <code>treg.to</code>: the web
|
||||
dashboard, the API, the call proxy, the <code>treg</code> CLI when pointed at our servers, and
|
||||
everything reachable from them (together, the <b>"Service"</b>).</p>
|
||||
<div class="note">
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<span class="brand">▚ tools-registry</span>
|
||||
<span class="sub">Dashboard tour — everything you can do in the web UI</span>
|
||||
<span class="spacer"></span>
|
||||
<a class="btn" href="https://treg.superdesign.dev/tutorial">CLI tutorial →</a>
|
||||
<a class="btn" href="https://treg.to/tutorial">CLI tutorial →</a>
|
||||
<button class="btn" id="themeBtn">◐</button>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Decide **which tools each member may use**, and whether they may run CLIs **on t
|
||||
tutorial follows the same format as the [main tutorial](/tutorial): every step shows the **exact command**,
|
||||
the **expected output**, and **what to notice** — so it reads standalone. Copy each command and follow along.
|
||||
|
||||
We use the registry at `https://treg.superdesign.dev`.
|
||||
We use the registry at `https://treg.to`.
|
||||
|
||||
---
|
||||
|
||||
@@ -48,7 +48,7 @@ the `HOME=` prefix.
|
||||
```bash
|
||||
for u in tom sam; do
|
||||
mkdir -p ~/.treg-personas/$u
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.superdesign.dev
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.to
|
||||
done
|
||||
```
|
||||
|
||||
@@ -281,7 +281,7 @@ are rejected with a clear `422` so you never grant a typo.
|
||||
|
||||
# Part 4 — The same controls in the dashboard
|
||||
|
||||
Everything above is also point-and-click at `https://treg.superdesign.dev/` → **Team**.
|
||||
Everything above is also point-and-click at `https://treg.to/` → **Team**.
|
||||
|
||||
- **The members table** gains two cells per person:
|
||||
- **Tools** — shows `All` or `N tools`. Click it to open a checklist of *every* tool in the team;
|
||||
|
||||
@@ -8,7 +8,7 @@ member's machine safe.
|
||||
|
||||
Every step shows the **exact command**, the **expected output**, and a **"Notice:"** line explaining what
|
||||
happened and why it matters. Copy each command and follow along. The registry in the examples is
|
||||
`https://treg.superdesign.dev`; replace it with your own.
|
||||
`https://treg.to`; replace it with your own.
|
||||
|
||||
> This is a companion to the main [hands-on tutorial](/tutorial) (sign-in, teams, the proxy, skills). Read
|
||||
> that first if the words *tool*, *skill*, *org*, or *member* are new. For controlling **which** tools each
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
// ---- Setup ------------------------------------------------------------
|
||||
{ part: "Setup", who: "sys", title: "Simulate three people on one machine",
|
||||
explain: "We play three users on one laptop by giving each its own <code>HOME</code>, so each gets an isolated <code>~/.treg/config.json</code> pointed at the registry. In real life every person is on their own machine and drops the <code>HOME=</code> prefix.",
|
||||
cmd: `for u in tom bob alice; do\n mkdir -p ~/.treg-personas/$u\n HOME=~/.treg-personas/$u treg config --base-url https://treg.superdesign.dev\ndone`,
|
||||
cmd: `for u in tom bob alice; do\n mkdir -p ~/.treg-personas/$u\n HOME=~/.treg-personas/$u treg config --base-url https://treg.to\ndone`,
|
||||
out: `# each persona now points at the registry`,
|
||||
notice: "Prefix any command with <code>HOME=~/.treg-personas/<name></code> to act as that person." },
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
{ part: "Part 6 · Call shapes & skills", who: "alice", title: "The raw HTTP underneath",
|
||||
explain: "<code>treg call</code> is sugar. Under the hood it's a plain HTTP request to <code><proxy>/call/<upstream-url></code> with your token header - any language, any agent, <code>curl</code>.",
|
||||
cmd: `ATOK=$(python3 -c "import json;print(json.load(open('/Users/you/.treg-personas/alice/.treg/config.json'))['token'])")\ncurl -s -H "X-Treg-Token: $ATOK" \\\n "https://treg.superdesign.dev/call/https://postman-echo.com/get"`,
|
||||
cmd: `ATOK=$(python3 -c "import json;print(json.load(open('/Users/you/.treg-personas/alice/.treg/config.json'))['token'])")\ncurl -s -H "X-Treg-Token: $ATOK" \\\n "https://treg.to/call/https://postman-echo.com/get"`,
|
||||
out: `# the postman-echo JSON again, "authorization": "Bearer sk-demo-secret-123" injected -\n# just curl, no secret on the client.`,
|
||||
notice: "The whole product in one line: prefix any upstream URL with the proxy, send your token, treg swaps in the real credential." },
|
||||
|
||||
@@ -294,8 +294,8 @@
|
||||
|
||||
// ---- Part 8 - The dashboard -----------------------------------------
|
||||
{ part: "Part 9 · The dashboard", who: "tom", title: "The same registry, in the browser",
|
||||
explain: "Open <b>treg.superdesign.dev</b> and sign in with the <b>email code</b> door (the same one you used in the terminal): type your email → click <b>Email me a sign-in code</b> → <b>check your inbox</b> for the 6-digit code → paste it in and <b>Sign in</b>. You land on your team org - Tools shows the <code>echo</code> tool, Activity shows the call, and (since Tom is now super-admin) an <b>Admin</b> panel appears.",
|
||||
cmd: `open https://treg.superdesign.dev/`,
|
||||
explain: "Open <b>treg.to</b> and sign in with the <b>email code</b> door (the same one you used in the terminal): type your email → click <b>Email me a sign-in code</b> → <b>check your inbox</b> for the 6-digit code → paste it in and <b>Sign in</b>. You land on your team org - Tools shows the <code>echo</code> tool, Activity shows the call, and (since Tom is now super-admin) an <b>Admin</b> panel appears.",
|
||||
cmd: `open https://treg.to/`,
|
||||
out: `# Sign in with email → land on Superdesign\n# Tools → the echo tool (Copy a snippet · Try it live)\n# Activity → Alice's GET echo · 200\n# Admin → cross-tenant stats + orgs (super-admin only)`,
|
||||
notice: "The dashboard now does it all in the browser - create teams, invite members, add secrets, register tools & skills, plus the super-admin surface - not just read + call." },
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
// ---- Further, focused tutorials --------------------------------------
|
||||
{ part: "Further tutorials", who: "sys", title: "Two deep-dive tutorials",
|
||||
explain: "Two features have their own step-by-step tutorials - <b>Import & shell</b> and <b>Team access control</b>. Both are cards on the Tutorial page (← Tutorials, top left), and both also exist as plain markdown.",
|
||||
cmd: `open https://treg.superdesign.dev/tutorial-import-shell.md # import + shell + the security sandbox\nopen https://treg.superdesign.dev/tutorial-access.md # per-member tool access control`,
|
||||
cmd: `open https://treg.to/tutorial-import-shell.md # import + shell + the security sandbox\nopen https://treg.to/tutorial-access.md # per-member tool access control`,
|
||||
out: `# import-shell : treg upload clis (your machine's CLIs -> team tools) + treg shell (stripe/gh just work)\n# + the local-run sandbox (isolated user, egress allow-list, filesystem jail)\n# access : choose which tools each member may use + the local-run on/off toggle`,
|
||||
notice: "Pick them from the Tutorial page like this one, or open the markdown URLs directly (agent-friendly)." },
|
||||
];
|
||||
@@ -415,7 +415,7 @@
|
||||
const ACCESS = [
|
||||
{ part: "Setup", who: "sys", title: "Two dials, two people",
|
||||
explain: "Every member has two independent dials: <b>tool access</b> (<code>tool_access</code>: which tools they may touch - default <b>all</b>) and <b>local execution</b> (<code>local_run_enabled</code>: may they run a CLI on their own machine - default <b>on</b>). A withheld tool is closed through <i>every</i> door - proxy call, server run, local run. The <b>owner</b> is never restricted. We play two people on one machine: <b>Tom</b> (owner) and <b>Sam</b> (the new teammate we restrict).",
|
||||
cmd: `for u in tom sam; do\n mkdir -p ~/.treg-personas/$u\n HOME=~/.treg-personas/$u treg config --base-url https://treg.superdesign.dev\ndone`,
|
||||
cmd: `for u in tom sam; do\n mkdir -p ~/.treg-personas/$u\n HOME=~/.treg-personas/$u treg config --base-url https://treg.to\ndone`,
|
||||
out: `# each persona now points at the registry`,
|
||||
notice: "Prefix a command with <code>HOME=~/.treg-personas/<name></code> to act as that person. In real life each person is on their own machine and drops the prefix." },
|
||||
|
||||
|
||||
@@ -6,18 +6,18 @@ The whole registry, end to end. Every step shows the **exact command**, the **ex
|
||||
There are two companion versions of this same walkthrough, generated from one source
|
||||
(`src/treg/web/tutorial.js`):
|
||||
|
||||
- **In the dashboard** → sign in at `https://treg.superdesign.dev/` and open **Help → Tutorial**.
|
||||
- **Standalone** → `https://treg.superdesign.dev/tutorial`.
|
||||
- **In the dashboard** → sign in at `https://treg.to/` and open **Help → Tutorial**.
|
||||
- **Standalone** → `https://treg.to/tutorial`.
|
||||
|
||||
### Two focused, deep-dive tutorials
|
||||
|
||||
Two features have their own detailed, step-by-step tutorials (exact commands, real output, and how each
|
||||
was tested):
|
||||
|
||||
- **Import & shell** → `https://treg.superdesign.dev/tutorial-import-shell.md` — `treg upload clis` turns the
|
||||
- **Import & shell** → `https://treg.to/tutorial-import-shell.md` — `treg upload clis` turns the
|
||||
CLIs already on your machine into team tools; `treg shell` opens a shell where `stripe`, `gh`, `gcloud` …
|
||||
just work with the team key injected. Includes the local-run **security sandbox**.
|
||||
- **Team access control** → `https://treg.superdesign.dev/tutorial-access.md` — choose **which tools each member
|
||||
- **Team access control** → `https://treg.to/tutorial-access.md` — choose **which tools each member
|
||||
may use** and whether they may run CLIs **locally**, at invite time or any time later.
|
||||
|
||||
---
|
||||
@@ -61,7 +61,7 @@ the `HOME=` prefix.
|
||||
```bash
|
||||
for u in tom bob alice; do
|
||||
mkdir -p ~/.treg-personas/$u
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.superdesign.dev
|
||||
HOME=~/.treg-personas/$u treg config --base-url https://treg.to
|
||||
done
|
||||
```
|
||||
|
||||
@@ -469,7 +469,7 @@ token header - any language, any agent, `curl`.
|
||||
```bash
|
||||
ATOK=$(python3 -c "import json;print(json.load(open('/Users/you/.treg-personas/alice/.treg/config.json'))['token'])")
|
||||
curl -s -H "X-Treg-Token: $ATOK" \
|
||||
"https://treg.superdesign.dev/call/https://postman-echo.com/get"
|
||||
"https://treg.to/call/https://postman-echo.com/get"
|
||||
```
|
||||
```
|
||||
# the postman-echo JSON again, "authorization": "Bearer sk-demo-secret-123" injected -
|
||||
@@ -759,13 +759,13 @@ Admin panel lights up for him.
|
||||
|
||||
## Step 33 - The same registry, in the browser
|
||||
|
||||
Open **treg.superdesign.dev** and sign in with the **email code** door (the same one you used in the terminal):
|
||||
Open **treg.to** and sign in with the **email code** door (the same one you used in the terminal):
|
||||
type your email → click **Email me a sign-in code** → **check your inbox** for the 6-digit code → paste it
|
||||
in and **Sign in**. You land on your team org - Tools shows the `echo` tool, Activity shows the call, and
|
||||
(since Tom is now super-admin) an **Admin** panel appears.
|
||||
|
||||
```bash
|
||||
open https://treg.superdesign.dev/
|
||||
open https://treg.to/
|
||||
```
|
||||
```
|
||||
# Sign in with email → land on Superdesign
|
||||
|
||||
+2
-2
@@ -60,7 +60,7 @@ def test_admin_and_skill_parsers():
|
||||
|
||||
def test_config_v2_roundtrip(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(cli, "CONFIG_PATH", tmp_path / "config.json")
|
||||
cli._save_config({"base_url": "https://treg.superdesign.dev", "token": "T", "email": "me@x.dev",
|
||||
cli._save_config({"base_url": "https://treg.to", "token": "T", "email": "me@x.dev",
|
||||
"active_org": "team-a", "identity": True})
|
||||
cfg = cli._load_config()
|
||||
assert cfg["token"] == "T" and cfg["active_org"] == "team-a" and cfg["identity"] is True
|
||||
@@ -69,7 +69,7 @@ def test_config_v2_roundtrip(tmp_path, monkeypatch):
|
||||
def test_legacy_multiorg_config_migrates(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(cli, "CONFIG_PATH", tmp_path / "config.json")
|
||||
(tmp_path / "config.json").write_text(json.dumps({
|
||||
"base_url": "https://treg.superdesign.dev", "active_org": "team-a",
|
||||
"base_url": "https://treg.to", "active_org": "team-a",
|
||||
"orgs": {"team-a": {"token": "OLD", "org_id": 3}}}))
|
||||
cfg = cli._load_config()
|
||||
assert cfg["token"] == "OLD" and cfg["active_org"] == "team-a" and cfg["identity"] is False
|
||||
|
||||
@@ -13,8 +13,8 @@ def test_collect_hosts_includes_registry_and_catalog_dedup_sorted():
|
||||
{"base_url": "https://api.stripe.com/v2"}, # same host, different path → deduped
|
||||
{"base_url": None}, # ignored
|
||||
]
|
||||
hosts = egress.collect_hosts("https://treg.superdesign.dev", catalog)
|
||||
assert hosts == ["api.github.com", "api.stripe.com", "treg.superdesign.dev"]
|
||||
hosts = egress.collect_hosts("https://treg.to", catalog)
|
||||
assert hosts == ["api.github.com", "api.stripe.com", "treg.to"]
|
||||
|
||||
|
||||
def test_collect_hosts_tolerates_bare_host_and_missing_registry():
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
"""The legacy-host redirect (treg.superdesign.dev → treg.to).
|
||||
|
||||
Only browser-facing marketing/doc pages 301 to the canonical host. API surfaces must be served in
|
||||
place on the legacy host forever: installed CLIs/skills point there with Bearer tokens, and HTTP
|
||||
clients strip Authorization on a cross-host redirect (some MCP clients follow no redirects at all).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from treg.api import app
|
||||
|
||||
|
||||
from treg.config import get_settings
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def raw_client(monkeypatch):
|
||||
"""No auth, no upstream — routing behavior only. Host is set per-request. The test env's
|
||||
public_url is localhost; pin it to production's so the Location assertions mean something."""
|
||||
monkeypatch.setenv("TREG_PUBLIC_URL", "https://treg.to")
|
||||
get_settings.cache_clear()
|
||||
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://registry") as c:
|
||||
yield c
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
LEGACY = {"host": "treg.superdesign.dev"}
|
||||
|
||||
|
||||
async def test_marketing_pages_redirect_to_canonical(raw_client):
|
||||
for path in ("/", "/terms", "/privacy", "/support", "/tutorial", "/login"):
|
||||
r = await raw_client.get(path, headers=LEGACY)
|
||||
assert r.status_code == 301, path
|
||||
assert r.headers["location"] == f"https://treg.to{path}", path
|
||||
|
||||
|
||||
async def test_query_string_survives_the_redirect(raw_client):
|
||||
r = await raw_client.get("/?utm_source=x", headers=LEGACY)
|
||||
assert r.status_code == 301
|
||||
assert r.headers["location"] == "https://treg.to/?utm_source=x"
|
||||
|
||||
|
||||
async def test_api_surfaces_are_served_in_place_on_the_legacy_host(raw_client):
|
||||
# Never redirected — a 301 here would strand every installed client. Whatever these routes
|
||||
# answer (200, 401, 405…), it must not be a redirect off-host.
|
||||
for path in ("/meta", "/llms.txt", "/install.sh", "/selfhost.sh", "/skill.md",
|
||||
"/catalog/search", "/auth/me", "/billing/stripe/webhook"):
|
||||
r = await raw_client.get(path, headers=LEGACY)
|
||||
assert r.status_code != 301, path
|
||||
|
||||
|
||||
async def test_post_is_never_redirected(raw_client):
|
||||
r = await raw_client.post("/", headers=LEGACY)
|
||||
assert r.status_code != 301
|
||||
|
||||
|
||||
async def test_canonical_host_is_untouched(raw_client):
|
||||
r = await raw_client.get("/", headers={"host": "treg.to"})
|
||||
assert r.status_code == 200
|
||||
@@ -418,7 +418,7 @@ def test_a_client_trusting_only_the_system_roots_rejects_our_leaf(ca):
|
||||
|
||||
# ---- P1 · the environment we publish -------------------------------------------------------
|
||||
def test_proxy_env_carries_the_flags_that_matter(ca):
|
||||
env = lp.proxy_env(18791, "tok en/+", ca.bundle_path, "treg.superdesign.dev")
|
||||
env = lp.proxy_env(18791, "tok en/+", ca.bundle_path, "treg.to")
|
||||
assert env["HTTPS_PROXY"] == "http://treg:tok%20en%2F%2B@127.0.0.1:18791"
|
||||
assert env["https_proxy"] == env["HTTPS_PROXY"] # curl reads lowercase
|
||||
assert env["NODE_USE_ENV_PROXY"] == "1" # Node's fetch ignores the proxy without it
|
||||
@@ -427,7 +427,7 @@ def test_proxy_env_carries_the_flags_that_matter(ca):
|
||||
"DENO_CERT", "AWS_CA_BUNDLE"):
|
||||
assert env[key] == str(ca.bundle_path)
|
||||
# Loopback and the registry itself must never come back through us.
|
||||
assert "127.0.0.1" in env["NO_PROXY"] and "treg.superdesign.dev" in env["NO_PROXY"]
|
||||
assert "127.0.0.1" in env["NO_PROXY"] and "treg.to" in env["NO_PROXY"]
|
||||
assert env["no_proxy"] == env["NO_PROXY"]
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -644,7 +644,7 @@ def test_a_relayed_402_carries_NO_link_out(clients):
|
||||
real = {"detail": {
|
||||
"error": "insufficient_balance",
|
||||
"message": ("akta.companies.enrich would cost ~$0.875 on treg's akta key and this team's "
|
||||
"balance is $0.5765.\n add funds: https://treg.superdesign.dev/app#billing"
|
||||
"balance is $0.5765.\n add funds: https://treg.to/app#billing"
|
||||
"\n or use your own key: treg connections connect --provider akta"),
|
||||
"balance_micro": 576500, "estimated_cost_micro": 875000,
|
||||
"topup_url": "/app#billing", "provider": "akta"}}
|
||||
@@ -662,7 +662,7 @@ def test_stripping_the_link_keeps_the_DIAGNOSIS(clients):
|
||||
real = {"detail": {
|
||||
"error": "insufficient_balance",
|
||||
"message": ("akta.companies.enrich would cost ~$0.875 and this team's balance is $0.5765."
|
||||
"\n add funds: https://treg.superdesign.dev/app#billing"
|
||||
"\n add funds: https://treg.to/app#billing"
|
||||
"\n or use your own key: treg connections connect --provider akta"),
|
||||
"balance_micro": 576500, "estimated_cost_micro": 875000}}
|
||||
out = _without_purchase_pointers(real)
|
||||
|
||||
@@ -21,7 +21,7 @@ def test_json_agents_write_user_global_header_config(tmp_path, monkeypatch):
|
||||
(cur / "mcp.json").write_text(json.dumps({"mcpServers": {"other": {"url": "http://x"}}}))
|
||||
(tmp_path / ".config" / "opencode").mkdir(parents=True)
|
||||
|
||||
out = mcp_install.install_mcp(base_url="https://treg.superdesign.dev", token="TESTKEY",
|
||||
out = mcp_install.install_mcp(base_url="https://treg.to", token="TESTKEY",
|
||||
only=["cursor", "opencode"])
|
||||
got = {d: (s, detail) for d, s, detail in out["results"]}
|
||||
assert got["Cursor"][0] == "ok" and got["opencode"][0] == "ok", out
|
||||
@@ -29,7 +29,7 @@ def test_json_agents_write_user_global_header_config(tmp_path, monkeypatch):
|
||||
cursor = json.loads((cur / "mcp.json").read_text())
|
||||
assert cursor["mcpServers"]["other"] == {"url": "http://x"} # untouched
|
||||
treg = cursor["mcpServers"]["treg"]
|
||||
assert treg["url"] == "https://treg.superdesign.dev/mcp/"
|
||||
assert treg["url"] == "https://treg.to/mcp/"
|
||||
assert treg["headers"]["Authorization"] == "Bearer TESTKEY"
|
||||
|
||||
oc = json.loads((tmp_path / ".config" / "opencode" / "opencode.json").read_text())
|
||||
@@ -37,7 +37,7 @@ def test_json_agents_write_user_global_header_config(tmp_path, monkeypatch):
|
||||
assert oc["mcp"]["treg"]["headers"]["Authorization"] == "Bearer TESTKEY"
|
||||
|
||||
# idempotent: a second run leaves exactly one entry, still correct
|
||||
mcp_install.install_mcp(base_url="https://treg.superdesign.dev", token="TESTKEY", only=["cursor"])
|
||||
mcp_install.install_mcp(base_url="https://treg.to", token="TESTKEY", only=["cursor"])
|
||||
again = json.loads((cur / "mcp.json").read_text())
|
||||
assert list(again["mcpServers"].keys()) == ["other", "treg"]
|
||||
|
||||
@@ -46,7 +46,7 @@ def test_uninstalled_agents_are_skipped_not_written(tmp_path, monkeypatch):
|
||||
"""No marker dir → the agent isn't touched (no stray config created for something not installed)."""
|
||||
monkeypatch.setattr(mcp_install, "HOME", tmp_path)
|
||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))
|
||||
out = mcp_install.install_mcp(base_url="https://treg.superdesign.dev", token="K",
|
||||
out = mcp_install.install_mcp(base_url="https://treg.to", token="K",
|
||||
only=["cursor", "opencode"])
|
||||
assert out["results"] == [] # nothing installed → nothing written
|
||||
assert not (tmp_path / ".cursor").exists()
|
||||
@@ -63,8 +63,8 @@ def test_the_mcp_url_carries_the_trailing_slash(tmp_path, monkeypatch):
|
||||
guard away from writing outside its sandbox."""
|
||||
monkeypatch.setattr(mcp_install, "HOME", tmp_path)
|
||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))
|
||||
out = mcp_install.install_mcp(base_url="https://treg.superdesign.dev/", token="K", only=[])
|
||||
assert out["mcp_url"] == "https://treg.superdesign.dev/mcp/"
|
||||
out = mcp_install.install_mcp(base_url="https://treg.to/", token="K", only=[])
|
||||
assert out["mcp_url"] == "https://treg.to/mcp/"
|
||||
|
||||
|
||||
def test_only_empty_means_NONE_not_all(tmp_path, monkeypatch):
|
||||
@@ -75,7 +75,7 @@ def test_only_empty_means_NONE_not_all(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))
|
||||
(tmp_path / ".cursor").mkdir() # cursor IS "installed" in this home…
|
||||
(tmp_path / ".config" / "opencode").mkdir(parents=True)
|
||||
out = mcp_install.install_mcp(base_url="https://treg.superdesign.dev", token="K", only=[])
|
||||
out = mcp_install.install_mcp(base_url="https://treg.to", token="K", only=[])
|
||||
assert out["results"] == [] and out["manual"] == [] # …and still nothing is written
|
||||
assert not (tmp_path / ".cursor" / "mcp.json").exists()
|
||||
assert not (tmp_path / ".config" / "opencode" / "opencode.json").exists()
|
||||
@@ -112,6 +112,6 @@ def test_cmd_mcp_install_REFUSES_a_bad_token_before_writing(tmp_path, monkeypatc
|
||||
|
||||
with pytest.raises(SystemExit) as e:
|
||||
cli.cmd_mcp_install(SimpleNamespace(name=None),
|
||||
{"token": "K", "base_url": "https://treg.superdesign.dev"})
|
||||
{"token": "K", "base_url": "https://treg.to"})
|
||||
assert "rejected" in str(e.value)
|
||||
assert not (tmp_path / ".config" / "opencode" / "opencode.json").exists() # nothing written
|
||||
|
||||
@@ -43,7 +43,7 @@ def test_no_unsubstituted_placeholder_reaches_the_plugin():
|
||||
plugin, so shipping the literal would break every URL in the most-read page of the product."""
|
||||
text = SKILL.read_text(encoding="utf-8")
|
||||
assert "{BASE}" not in text
|
||||
assert "https://treg.superdesign.dev/install.sh" in text
|
||||
assert "https://treg.to/install.sh" in text
|
||||
|
||||
|
||||
def test_the_skill_maps_itself_onto_the_MCP_TOOLS(manifest):
|
||||
|
||||
@@ -32,10 +32,10 @@ def test_local_mode_needs_sqlite_and_a_loopback_url():
|
||||
# a real deploy — either half is enough to refuse
|
||||
assert _settings(database_url="postgresql+asyncpg://u@h/db").single_user_ok is False, \
|
||||
"a Postgres URL means a real deploy: no-login must be off"
|
||||
assert _settings(public_url="https://treg.superdesign.dev").single_user_ok is False, \
|
||||
assert _settings(public_url="https://treg.to").single_user_ok is False, \
|
||||
"a public domain must never serve a no-login dashboard"
|
||||
assert _settings(database_url="postgresql+asyncpg://u@h/db",
|
||||
public_url="https://treg.superdesign.dev").single_user_ok is False
|
||||
public_url="https://treg.to").single_user_ok is False
|
||||
|
||||
|
||||
def test_it_is_off_unless_explicitly_asked_for():
|
||||
|
||||
Reference in New Issue
Block a user