NOTICE: All information contained herein is, and remains
the property of TechnoCore Automate.
Enterprise URL shortener with branded QR codes, analytics,
A/B split testing, geo tracking, password protection, click
webhooks, custom QR colours, and campaign management.
ObjGo maps short codes to target URLs. Each mapping
optionally has a QR code PNG (with branded logo overlay
and custom colours), a hit counter, and expiry rules (by
hit count or datetime). Mappings can be grouped into GoSets
that trigger post-mapping SQL hooks.
Inherits from ObjData.ObjData.
All settings live under the go: section in config.yaml.
| Key | Default | Purpose |
|---|---|---|
custom_domain |
-- | Custom domain for short links (overrides $baseurl$). Example: https://tcgo.co.za |
qr_enabled |
true |
Enable/disable QR PNG generation globally |
branded_qr |
true |
Composite the package logo into the centre of QR codes |
rate_limit_rpm |
60 |
Maximum redirects per minute per IP address |
api_keys |
-- | Dict of package: api_key pairs for mutating REST endpoints |
brand_name |
tcgo.co.za |
Landing page and footer brand name |
brand_tagline |
Short Links. Built for Business. |
Hero section tagline |
brand_description |
(long default) | Meta description and hero paragraph |
brand_footer |
TechnoCore Automate |
Footer attribution text |
qr_fg_color |
#000000 |
Default QR foreground colour (hex). Overridable per GoSet in def_go.QrFgColor |
qr_bg_color |
#FFFFFF |
Default QR background colour (hex). Overridable per GoSet in def_go.QrBgColor |
geoip_db |
-- | Path to MaxMind .mmdb file for geo resolution |
Also uses:
| Section | Key | Purpose |
|---|---|---|
site |
url |
Base URL prepended to relative targets |
Definition table for GoSet groups. Holds per-group workflows
and SQL hooks.
| Column | Type | Purpose |
|---|---|---|
Shortcode |
char(50) | GoSet short code (PK with Package) |
go_id |
int(11) | Auto-increment ID |
Package |
char(64) | Owning package |
Module |
char(255) | Associated module |
GoSet |
char(255) | Group identifier |
MapPostSql |
text | SQL executed on every find_map hit |
AddPostSql |
text | SQL executed when add_maps assigns a code |
Workflow |
char(255) | Workflow reference |
TimeAdded |
datetime | Row creation timestamp |
Url |
char(255) | Default URL for the group |
Company |
char(255) | Company reference |
Hits |
int(11) | Group hit counter |
HitsExpires |
int(11) | Group hit limit |
AltUrl |
char(255) | Alternative URL |
TimeHit |
datetime | Last hit timestamp |
TimeExpires |
datetime | Group expiry datetime |
QrFgColor |
char(7) | Per-GoSet QR foreground hex colour |
QrBgColor |
char(7) | Per-GoSet QR background hex colour |
WebhookUrl |
char(512) | Click webhook endpoint URL |
WebhookInterval |
int(11) | Fire webhook every N clicks (0 = disabled) |
Individual short-code-to-URL mappings.
| Column | Type | Purpose |
|---|---|---|
go_id |
int(11) | Auto-increment ID |
Package |
char(64) | Owning package |
ShortCode |
char(50) | The short code (indexed) |
Url |
text | Target URL |
GoSet |
char(255) | Group identifier (indexed) |
Hits |
int(11) | Click counter |
HitsExpires |
int(11) | Expire after N clicks (0 = unlimited) |
TimeCreated |
datetime | Creation timestamp |
TimeExpires |
datetime | Expiry datetime (NULL = never) |
LastAccessed |
datetime | Last redirect timestamp |
Param1 |
char(255) | Custom parameter 1 |
Param2 |
char(255) | Custom parameter 2 |
Param3 |
char(255) | Custom parameter 3 |
SplitVariants |
text | JSON A/B split config: {"urls":[...], "weights":[...]} |
Password |
char(255) | SHA256 password hash |
PasswordRequired |
char(1) | Y if password-protected, N otherwise |
Append-only access log. Every successful find_map inserts a row.
| Column | Type | Purpose |
|---|---|---|
Guid |
char(50) | Unique row ID (PK) |
ShortCode |
char(255) | Which code was accessed (indexed) |
Url |
text | Target URL at time of click |
TimeAccessed |
datetime | Click timestamp |
user_ip |
char(255) | Visitor IP address |
user_agent |
char(255) | Browser user-agent string |
user_referer |
char(255) | HTTP referer header |
user_browser |
char(255) | Parsed browser name |
Param1 |
char(255) | Custom parameter 1 |
Param2 |
char(255) | Custom parameter 2 |
Param3 |
char(255) | Custom parameter 3 |
user_os |
char(255) | Parsed OS name |
Variant |
char(10) | A/B split variant label (A, B, C, ...) |
Country |
char(100) | Geo-resolved country |
City |
char(100) | Geo-resolved city |
__init__(db, server) -> NoneInitialise with database handle and optional server reference.
Calls create_tables_from_yaml() to ensure all three tables
exist.
_validate_short_code(code: str) -> tuple[bool, str]Validate a custom short code. Rules:
RESERVED_CODES (case-insensitive):go, api, qr, health, status, dashboard,favicon.ico, logo.png, static.^[A-Za-z0-9_-]+$.PROFANITY_BLOCKLIST.Returns (valid, error_message).
find_short_code(url: str) -> strFind the existing short code for a URL, or generate a new
unique one (minimum 5-character UUID prefix). Increments
prefix length on collision.
map(short_code, target_url, hits_expires, time_expires, go_set, params, password) -> tuple[str, str]Create a new mapping. Validates the target URL with
validators.url(). Prepends site.url to relative URLs.
When password is provided, stores a SHA256 hash. Returns
(out_url, qr_url) or ("", "") on failure. Rejects
reserved short codes.
find_map(short_code, password, utm_params, client_ip, user_agent) -> tuple[str, str]Look up a short code. Flow:
("", "AUTH") ifdata_go with expiry checks.("", "NONE") if not found / expired.data_go_track with variant and geo data.MapPostSql hook.(url, "PASS").Returns ("", "ERR") for empty input.
map_split(short_code, variants, weights, go_set) -> tuple[str, str]Create an A/B split-test mapping. variants is a list of
target URLs, weights is a list of integer percentages
(e.g. [70, 30]). Stores JSON in SplitVariants.
Returns (out_url, qr_url).
verify_password(short_code, password) -> boolVerify a password for a protected link. SHA256 hash comparison.
update_url(short_code: str, new_url: str) -> boolUpdate the target URL for an existing short code. Validates
with validators.url(). Returns True on success.
unmap(short_code: str) -> NoneDelete a mapping from data_go.
qr_map(short_code, fg_color, bg_color) -> strGenerate (or locate) a QR PNG under
local.documents/qr/package.<pkg>/. Returns the filesystem
path. Disabled when go.qr_enabled is false. Applies custom
QR colours. Attempts branded overlay when go.branded_qr is
enabled.
qr_map_branded(short_code: str) -> strGenerate a branded QR PNG with the package logo composited
in the centre (20% of QR size). Falls back to plain QR
when PIL is unavailable or no logo is configured.
get_base_url() -> strReturn the base URL for short links. Checks
go.custom_domain first; falls back to $baseurl$.
Strips trailing slash.
_resolve_geo(ip_address: str) -> tuple[str, str]Resolve an IP to (country, city) via ObjGeoLocation.
Returns ("", "") on failure or for loopback addresses
(127.0.0.1, ::1, empty).
_append_utm(url, utm_params) -> strAppend recognised UTM query params (utm_source,
utm_medium, utm_campaign, utm_content, utm_term)
to a target URL. Preserves existing query string.
_hex_to_rgb(hex_color) -> tuple[int, int, int]Convert a hex colour string (e.g. #FF0000) to an RGB
tuple. Falls back to black (0, 0, 0) on error.
_resolve_qr_colors(fg_color, bg_color) -> tupleResolve QR foreground and background colours. Priority:
fg_color/bg_color arguments.def_go.QrFgColor/QrBgColor.go.qr_fg_color/go.qr_bg_color._check_and_fire_webhook(short_code, url, hits, country, city) -> NoneCheck if the GoSet has a WebhookUrl configured in def_go.
If WebhookInterval > 0 and hits % interval == 0, fire the
webhook in a daemon thread. Payload includes short_code, url,
hits, country, city, and UTC timestamp.
_fire_click_webhook(webhook_url, payload) -> NonePOST a JSON payload to a webhook URL. Runs in a daemon thread
so redirects are not delayed. 10-second timeout.
bulk_create(entries: list[dict]) -> list[dict]Bulk-create short-code mappings with QR codes. Each entry
must have keys: short_code, url, go_set, params
(optional). Returns a list of result dicts:
{short_code, short_url, qr_url, qr_path}.
bulk_export_zip(short_codes: list[str]) -> strExport QR PNGs for all given short codes as a ZIP file.
Generates any missing QR codes first, then bundles them into
local.documents/qr/. Returns the path to the zip file.
add_maps() -> NoneMap all data_go rows with NULL/empty ShortCode. Generates
codes and runs the GoSet AddPostSql hook for each.
patch_param(text: str, depth: int) -> strReplace $param1$-$param3$ and $group$ placeholders.
track(parameter, referer, remote_ip) -> strPlaceholder for future tracking logic.
micro_service(thread_count: str) -> NoneLaunch the go micro-service (placeholder).
# Map all unmapped URLs
python factory.core/ObjGo.py stage
# Run the microservice
python factory.core/ObjGo.py service
# Bulk-create mappings from CSV
python factory.core/ObjGo.py bulk-create --csv data.csv
# Export QR codes for a GoSet as ZIP
python factory.core/ObjGo.py bulk-qr --go-set campaign_q2
Served by factory.pages/WebPageGo.py:
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | / |
-- | Landing page with branding |
| POST | /go/ |
-- | Create mapping (legacy JSON) |
| GET | /go/{code} |
-- | Redirect (303) to target |
| GET | /g/{code} |
-- | Alias for /go/{code} |
| GET | /~/{code} |
-- | Alias for /go/{code} |
| GET | /map/{code} |
-- | Alias for /go/{code} |
| POST | /go/{code} |
-- | Submit password for protected link |
| GET | /go/{code}?preview=1 |
-- | Preview page with QR and stats |
| GET | /qr/{code} |
-- | Serve branded QR code PNG |
| POST | /api/go/create |
x-api-key | Create mapping via REST API |
| PUT | /api/go/{code} |
x-api-key | Update target URL |
| DELETE | /api/go/{code} |
x-api-key | Delete mapping |
| GET | /api/go/list |
-- | List mappings (paginated) |
| GET | /api/go/{code}/stats |
-- | Stats with geo and A/B data |
| GET | /api/go/search?q={term} |
-- | Search by code, URL, GoSet |
| GET | /api/go/health |
-- | Health check with system metrics |
| GET | /go/dashboard |
-- | Analytics dashboard (HTML/JSON) |
| GET | /go/status |
-- | Service status dashboard (HTML/JSON) |
| GET | /go/docs |
-- | API reference page |
| GET | /go/import |
-- | Bulk CSV import form |
| POST | /go/import |
form api_key | Process bulk import |
| GET | /favicon.ico |
-- | Favicon |
| GET | /logo.png |
-- | Brand logo |
| GET | /logo-dark.png |
-- | Dark-mode brand logo |
go, api,qr, health, status, dashboard, favicon.ico,logo.png, static.[A-Za-z0-9_-] only.Create a split-test link via map_split() or POST to
/api/go/create with split_variants and split_weights:
{
"short_code": "promo",
"url": "https://a.com",
"split_variants": [
"https://a.com",
"https://b.com"
],
"split_weights": [70, 30]
}
On each click, find_map uses random.choices() with the
weights to select a variant. The chosen variant label (A, B,
C, ...) is stored in data_go_track.Variant. View split
stats via /api/go/{code}/stats.
Pass password when creating a mapping. The password is
SHA256-hashed and stored in data_go.Password. Visitors
see a password form before redirect. The form POSTs to the
same route; on correct password, find_map returns PASS.
Configure WebhookUrl and WebhookInterval on a GoSet in
def_go. Every time Hits % WebhookInterval == 0, a JSON
POST is fired in a daemon thread:
{
"short_code": "abc",
"url": "https://target.com",
"hits": 100,
"country": "ZA",
"city": "Cape Town",
"timestamp": "2026-05-02T12:00:00+00:00"
}
Priority order:
fg_color/bg_color arguments.QrFgColor/QrBgColor in def_go.go.qr_fg_color/go.qr_bg_color.On each redirect, the visitor IP is resolved to country and
city via ObjGeoLocation.geocode_ip(). Loopback addresses
(127.0.0.1, ::1) return empty strings. Results are stored
in data_go_track.Country and data_go_track.City.
The landing page, expiry page, and docs page read branding
from config.yaml under go.brand_* keys:
brand_name -- page title and headerbrand_tagline -- hero section taglinebrand_description -- meta descriptionbrand_footer -- footer attributioncythonize -3 -a -i ObjGo.py
Compiling factory.core/ObjGo.py
Updated: 2026-05-02