Feature Specification: Trademark & Reserved Names Portal
1. Overview
Currently, reserved names (like admin, google, stripe) are stored as a flat JSON array on individual TLD records in the database. This is difficult to manage at scale.
The goal of this feature is to build a centralized Trademark Portal. This will move reserved names into a dedicated relational database table, providing a global blocklist across all TLDs, and offering a Web UI for administrators (and eventually brands) to manage these protections.
2. Database Schema Changes (models.py)
2.1 New Table: ReservedName
This table acts as the global or per-TLD blocklist.
class ReservedName(db.Model):
__tablename__ = 'reserved_names'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False, index=True) # e.g., 'google', 'admin'
reason = db.Column(db.String(50), default='trademark') # 'trademark', 'system', 'premium'
is_global = db.Column(db.Boolean, default=True) # If True, blocks across ALL TLDs
tld_id = db.Column(db.Integer, db.ForeignKey('tlds.id'), nullable=True) # If not global, blocks on specific TLD
owner_id = db.Column(db.String(36), db.ForeignKey('users.id'), nullable=True) # The verified owner (if claimed)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
2.2 Data Migration Strategy
- A script will read the existing JSON
reserved_namesarray from theTLDtable. - It will insert those names into the new
ReservedNametable withis_global=True. - The old JSON column can be deprecated or removed.
3. Core Logic Changes (routes/domains.py)
Update the search and registration logic to query the new ReservedName table instead of the JSON array.
Search/Register Check Logic:
# Check if the SLD exists in the ReservedName table
reserved = ReservedName.query.filter_by(name=sld_name).filter(
(ReservedName.is_global == True) | (ReservedName.tld_id == tld.id)
).first()
if reserved:
# Check if the current user is the verified owner of this trademark
if user and reserved.owner_id == user.id:
# Allow registration (they own the trademark)
pass
else:
# Block registration
flash(f'This domain name is reserved ({reserved.reason}).', 'error')
4. Web UI for Administration
4.1 Admin Dashboard (/admin/trademarks)
Create a new route and template restricted to system administrators (e.g., users with an is_admin flag or specific user IDs).
- View: A paginated table of all reserved names, showing Name, Reason, Scope (Global vs Specific TLD), and Owner (if claimed).
- Add: A form to manually add a single reserved name or bulk-add via comma-separated list.
- Edit/Delete: Buttons to remove a block or assign an owner_id to a specific brand account.
5. Phase 2: Brand Self-Service Portal (Future Scope)
(Note: To be built after the Admin UI is stable)
- Claim Route (/claim-trademark): A brand can search for their blocked name and click "Claim".
- Verification: The system generates a unique TXT record (e.g., headless-verify=12345). The user must place this on their existing .com domain.
- Automated Unlock: Once the system verifies the TXT record, it automatically updates the owner_id in the ReservedName table, allowing the brand to register their domains across the ecosystem.
6. Implementation Steps for MVP
- Models: Create the
ReservedNametable and migration script. (Completed) - Logic Update: Update the search/register endpoints to query the new table. (Completed)
- Admin UI: Build a secure admin page (
/admin/trademarks) to view, add, and delete records from this table. - CSV Upload: Add a feature to the Admin UI to allow the admin to upload a CSV file (e.g., the Tranco Top 10k list) and bulk-insert them into the
ReservedNametable.