Abhrankan Chakrabarti
This document describes the current production-facing contracts for lab-api and crypto-lab as deployed today.
The service is intentionally small and intentionally narrow:
127.0.0.1:8088/api/* service provides health, application metadata, read-only mathematical calculations, and an authenticated system snapshot; it does not use a database/school/* surface contains a public static UI and a read-only SQLite API configured by SCHOOL_DB_PATH/school/api/health route are not authenticated; the remaining /school/api/ routes are protected by Nginx HTTP Basic Authenticationcrypto-lab under /crypto-api/* is a separate process for hash/HMAC demos only; it does not store keys or replace a KMSInternet
↓
HTTPS
↓
Nginx
├── /api/* and /school/* → 127.0.0.1:8088 → lab-api → systemd
└── /crypto-api/* → 127.0.0.1:8089 → crypto-lab → systemd
https://abhrankan.duckdns.org/api/health
https://abhrankan.duckdns.org/api/v1/info
https://abhrankan.duckdns.org/api/v1/math/catalan/10
https://abhrankan.duckdns.org/api/v1/math/fibonacci/10
https://abhrankan.duckdns.org/api/v1/math/gcd/84/30
https://abhrankan.duckdns.org/api/v1/math/is-prime/97
https://abhrankan.duckdns.org/api/v1/math/next-prime/100
https://abhrankan.duckdns.org/api/v1/math/previous-prime/100
https://abhrankan.duckdns.org/api/v1/math/prime-gap/1000
https://abhrankan.duckdns.org/api/v1/math/prime-pi/1000
https://abhrankan.duckdns.org/api/v1/math/pi/1000
https://abhrankan.duckdns.org/api/v1/math/factor/360
https://abhrankan.duckdns.org/api/v1/math/totient/36
https://abhrankan.duckdns.org/api/v1/math/mobius/30
https://abhrankan.duckdns.org/api/v1/math/divisor-count/360
https://abhrankan.duckdns.org/api/v1/math/divisor-sum/360
https://abhrankan.duckdns.org/api/v1/math/divisors/360
https://abhrankan.duckdns.org/api/v1/snapshot
https://abhrankan.duckdns.org/school/
https://abhrankan.duckdns.org/school/api/health
https://abhrankan.duckdns.org/school/api/tables
https://abhrankan.duckdns.org/school/api/tables/II_A/students/1001
https://abhrankan.duckdns.org/school/api/admin/tables/II_A/students/1001
https://abhrankan.duckdns.org/school/api/admin/audit
These are consumed through Nginx, which terminates TLS and forwards traffic to the backend.
http://127.0.0.1:8088/health
http://127.0.0.1:8088/v1/info
http://127.0.0.1:8088/v1/math/catalan/10
http://127.0.0.1:8088/v1/math/fibonacci/10
http://127.0.0.1:8088/v1/math/gcd/84/30
http://127.0.0.1:8088/v1/math/is-prime/97
http://127.0.0.1:8088/v1/math/next-prime/100
http://127.0.0.1:8088/v1/math/previous-prime/100
http://127.0.0.1:8088/v1/math/prime-gap/1000
http://127.0.0.1:8088/v1/math/prime-pi/1000
http://127.0.0.1:8088/v1/math/pi/1000
http://127.0.0.1:8088/v1/math/factor/360
http://127.0.0.1:8088/v1/math/totient/36
http://127.0.0.1:8088/v1/math/mobius/30
http://127.0.0.1:8088/v1/math/divisor-count/360
http://127.0.0.1:8088/v1/math/divisor-sum/360
http://127.0.0.1:8088/v1/math/divisors/360
http://127.0.0.1:8088/v1/snapshot
http://127.0.0.1:8088/school/
http://127.0.0.1:8088/school/api/health
http://127.0.0.1:8088/school/api/tables
http://127.0.0.1:8088/school/api/tables/II_A/students/1001
http://127.0.0.1:8088/school/api/admin/tables/II_A/students/1001
http://127.0.0.1:8088/school/api/admin/audit
The backend itself is only accessible from the local machine. It should not be exposed directly on a public interface or a public port.
The routes in this table are backend paths. Public clients prefix core routes with /api. School routes already include their /school prefix.
| Method | Endpoint | Auth (public edge) | Purpose |
|---|---|---|---|
| GET | /health |
None | Service health |
| GET | /v1/info |
None | Non-sensitive application metadata and endpoint discovery |
| GET | /v1/math/catalan/:n |
None | Catalan number, 0 ≤ n ≤ 34 |
| GET | /v1/math/fibonacci/:n |
None | Fibonacci number, 0 ≤ n ≤ 186 |
| GET | /v1/math/gcd/:a/:b |
None | Greatest common divisor of two u64 values |
| GET | /v1/math/is-prime/:n |
None | Primality test, 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/next-prime/:n |
None | Next prime greater than n, 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/previous-prime/:n |
None | Previous prime less than n, 2 < n ≤ 1,000,000 |
| GET | /v1/math/prime-gap/:n |
None | Surrounding prime gap, 2 < n ≤ 1,000,000 |
| GET | /v1/math/prime-pi/:n |
None | Prime-counting function π(n), 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/pi/:n |
None | Prime-counting function π(n), compatibility alias |
| GET | /v1/math/factor/:n |
None | Prime factorisation, 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/totient/:n |
None | Euler’s totient φ(n), 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/mobius/:n |
None | Möbius function μ(n), 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/divisor-count/:n |
None | Divisor-count function τ(n), 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/divisor-sum/:n |
None | Divisor-sum function σ(n), 0 ≤ n ≤ 1,000,000 |
| GET | /v1/math/divisors/:n |
None | Sorted positive divisors of n, 0 ≤ n ≤ 1,000,000 |
| GET | /v1/catalan/:n |
None | Deprecated Catalan compatibility alias; use /v1/math/catalan/:n, 0 ≤ n ≤ 34 |
| GET | /v1/snapshot |
Nginx Basic Auth | Host/system snapshot |
| GET | /school/ |
None | Public School portal UI and static assets |
| GET | /school/api/health |
None | School API health |
| GET | /school/api/tables |
Nginx Basic Auth | Available School tables |
| GET | /school/api/tables/{table} |
Nginx Basic Auth | Paginated, searchable safe student rows |
| GET | /school/api/tables/{table}/schema |
Nginx Basic Auth | Discovered table schema |
| GET | /school/api/tables/{table}/students/{student_id} |
Nginx Basic Auth | Privacy-filtered student detail |
| GET | /school/api/admin/tables/{table}/students/{student_id} |
Nginx Basic Auth + admin allowlist | Full student detail and audit event |
| GET | /school/api/admin/audit |
Nginx Basic Auth + admin allowlist | Paginated admin student-detail audit events |
{table} is a discovered class table name (for example II_A, LPP). {student_id} is the table’s primary-key value (for example Student Code or Roll No), not a nested path segment named students.
The core /api/* service does not use a database. The separate School API under /school/ reads SQLite from SCHOOL_DB_PATH and is read-only during normal runtime. The /school/ UI is served from the backend’s static/school directory.
In production, the public /school/ location serves the UI and static assets without authentication. The exact /school/api/health location is also public for health checks. Nginx Basic Authentication applies to /school/api/ for table, schema, student, admin, and audit requests. The Rust process itself does not enforce Basic Authentication; it assumes that Nginx provides this edge protection. Admin detail and audit add an application allowlist on top of that.
The Nginx exact health location takes precedence over the broader protected /school/api/ prefix:
location = /school/api/health {
proxy_pass http://127.0.0.1:8088;
}
location /school/api/ {
auth_basic "School Database";
auth_basic_user_file /etc/nginx/school.htpasswd;
proxy_pass http://127.0.0.1:8088;
proxy_set_header X-Authenticated-User $remote_user;
}
location /school/ {
proxy_pass http://127.0.0.1:8088;
}
The /school/api/ proxy preserves the /school/api/... path when forwarding to Axum. The /school/ location is for the portal UI and static assets, not for bypassing API authorization.
GET /school/api/tables
GET /school/api/tables/{table}?limit=25&offset=0&search=Abhrankan
GET /school/api/tables/{table}/schema
All three routes require Nginx Basic Authentication. The table route returns
safe/display columns only and supports optional limit, offset, and search
query parameters. The default page size is 25 and the backend caps page sizes
at 100. Schema discovery returns the table’s columns and primary-key metadata.
GET /school/api/tables/{table}/students/{student_id}
Example:
GET /school/api/tables/II_A/students/1001
This endpoint excludes explicitly sensitive contact, financial, and identity fields.
GET /school/api/admin/tables/{table}/students/{student_id}
Example:
GET /school/api/admin/tables/II_A/students/1001
The admin endpoint returns every column discovered from the selected table schema. It requires both:
/school/api/ location.X-Authenticated-User header and in LAB_API_ADMIN_USERS.Configure the backend with a comma-separated allowlist, for example:
LAB_API_ADMIN_USERS=abhrankan,teacher1,principal
Missing authentication at Nginx yields 401 Unauthorized.
Authenticated but non-admin (or missing X-Authenticated-User) yields 403 Forbidden:
{
"error": "admin access required"
}
The backend listens only on 127.0.0.1:8088, so the authenticated identity header is intended to be supplied by the local Nginx reverse proxy rather than by an Internet client. Nginx should set it inside the authenticated /school/api/ location:
location /school/api/ {
auth_basic "School Database";
auth_basic_user_file /etc/nginx/school.htpasswd;
proxy_pass http://127.0.0.1:8088;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Authenticated-User $remote_user;
}
Do not expose the backend directly on a public interface. The public health route does not provide table or student data.
GET /school/api/admin/audit?limit=50&offset=0
This route requires both School API Basic Authentication and an authenticated
username listed in LAB_API_ADMIN_USERS. It returns recent admin student-detail
access events. limit defaults to 50 and is capped at 100; offset defaults
to 0. Admin student-detail responses are returned only after the corresponding
audit event is recorded.
| Status | Meaning |
|---|---|
| 200 OK | Successful request |
| 400 Bad Request | Invalid mathematical input, including values above the documented prime limits |
| 401 Unauthorized | Missing or invalid HTTP Basic credentials at Nginx for protected School/API routes or the snapshot |
| 403 Forbidden | Authenticated School user is not on the admin allowlist (admin detail only) |
| 404 Not Found | Route not defined, or unknown table/student |
| 500 Internal Server Error | Unexpected backend failure |
The most important contract checks are:
GET /health succeeds with 200GET /v1/info succeeds with 200 and returns non-sensitive metadataGET /v1/math/catalan/:n succeeds with 200 when 0 ≤ n ≤ 34GET /v1/math/fibonacci/:n succeeds with 200 when 0 ≤ n ≤ 186GET /v1/math/gcd/:a/:b succeeds with 200 for valid u64 path valuesGET /v1/math/is-prime/:n succeeds with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/next-prime/:n succeeds with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/previous-prime/:n succeeds with 200 when 2 < n ≤ 1,000,000GET /v1/math/prime-gap/:n succeeds with 200 when 2 < n ≤ 1,000,000GET /v1/math/prime-pi/:n and /v1/math/pi/:n succeed with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/factor/:n succeeds with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/totient/:n succeeds with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/mobius/:n succeeds with 200 when 0 ≤ n ≤ 1,000,000GET /v1/math/divisor-count/:n, /v1/math/divisor-sum/:n, and /v1/math/divisors/:n succeed with 200 when 0 ≤ n ≤ 1,000,000400 when n > 1,000,000GET /v1/catalan/:n remains available as a deprecated compatibility alias; new clients should use /v1/math/catalan/:nGET /v1/catalan/:n fails with 400 when n > 34GET /v1/math/fibonacci/:n fails with 400 when n > 186GET /v1/snapshot fails with 401 without valid Basic AuthGET /school/ succeeds without Basic Auth and serves the public portalGET /school/api/health succeeds without Basic AuthGET /school/api/tables, table, schema, student, admin, and audit routes fail with 401 without valid Basic Auth at the public edgeGET /school/api/admin/... fails with 403 for non-admin usersThe math endpoints are public, read-only GET routes. They use u128 arithmetic where applicable and return numeric values as strings to preserve the exact result in JSON clients.
GET /v1/math/catalan/:n
Supports 0 ≤ n ≤ 34. The existing GET /v1/catalan/:n route is retained as a deprecated compatibility alias with the same response and limit.
GET /v1/math/fibonacci/:n
Supports 0 ≤ n ≤ 186. F(186) is the largest Fibonacci value representable by the implementation’s u128 boundary; n = 187 returns 400 Bad Request.
Example response:
{
"n": 10,
"value": "55"
}
GET /v1/math/gcd/:a/:b
Computes the GCD of two u64 path values using the Euclidean algorithm. Zero is valid, including gcd(0, 0) = 0.
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/gcd/84/30'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/fibonacci/10'
{
"a": 84,
"b": 30,
"gcd": 6
}
GET /v1/math/is-prime/:n
GET /v1/math/next-prime/:n
GET /v1/math/previous-prime/:n
GET /v1/math/prime-gap/:n
GET /v1/math/prime-pi/:n
GET /v1/math/pi/:n
These public, read-only endpoints support values up to 1,000,000. The
/v1/math/pi/:n route is a compatibility alias for /v1/math/prime-pi/:n.
Prime-counting uses a bounded sieve so requests above the limit return
400 Bad Request rather than allocating unbounded memory. Prime searches and
prime gaps use the same upper bound.
Examples:
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/is-prime/97'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/next-prime/100'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/previous-prime/100'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/prime-gap/1000'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/prime-pi/1000'
{
"n": 97,
"prime": true
}
next-prime and prime-counting return { "n": ..., "value": "..." }.
previous-prime returns the greatest prime strictly less than n using the
same response shape. It returns 400 Bad Request for n ≤ 2.
prime-gap returns the previous prime, next prime, and gap:
{
"n": 1000,
"previous_prime": 997,
"next_prime": 1009,
"gap": 12
}
GET /v1/math/factor/:n
GET /v1/math/totient/:n
GET /v1/math/mobius/:n
GET /v1/math/divisor-count/:n
GET /v1/math/divisor-sum/:n
GET /v1/math/divisors/:n
These public, read-only endpoints support values from 0 through 1,000,000.
They use bounded trial division and are intended for demonstrations, not as a
general-purpose factoring service. Values above the limit return 400 Bad
Request.
Prime factors are returned in ascending order. The value 1 has no prime
factors, so factorisation returns an empty list for n = 1.
Factorisation example:
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/factor/360'
{
"n": 360,
"factors": [
{ "prime": 2, "power": 3 },
{ "prime": 3, "power": 2 },
{ "prime": 5, "power": 1 }
]
}
Totient and Möbius examples:
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/totient/36'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/mobius/30'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/divisor-count/360'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/divisor-sum/360'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/divisors/12'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/mobius/36'
{ "n": 36, "value": "12" }
{ "n": 30, "value": -1 }
{ "n": 36, "value": 0 }
The endpoint semantics are:
φ(0) = 0 and φ(1) = 1μ(0) = 0 and μ(1) = 1μ(n) = 0 when a prime square divides nμ(n) is 1 or -1 according to the parity of its distinct prime factorsThe divisor functions use the same factorisation:
τ(360) = 24 counts the positive divisors of 360σ(360) = 1170 sums the positive divisors of 360GET /v1/math/divisor-count/:n
GET /v1/math/divisor-sum/:n
Both routes support 0 ≤ n ≤ 1,000,000 and return the standard string-valued
mathematical response. For n = 0, both return 0; for n = 360, they
return 24 and 1170, respectively.
The divisors route returns all positive divisors in ascending order:
GET /v1/math/divisors/:n
For n = 12:
{
"n": 12,
"divisors": [1, 2, 3, 4, 6, 12]
}
For n = 0, divisors is an empty array. The list verifies
τ(n) = divisors.length and σ(n) = sum(divisors).
GET /v1/info
This public endpoint describes the running application and its public route surface. It does not require authentication and must not expose hostnames, filesystem paths, credentials, secret environment variables, or system snapshot data. The environment field is only a non-secret deployment label.
curl -sS 'https://abhrankan.duckdns.org/api/v1/info'
curl -sS 'http://127.0.0.1:8088/v1/info'
{
"service": "lab-api",
"api_version": "v1",
"app_version": "0.9.2",
"endpoints": [
"GET /health",
"GET /v1/info",
"GET /v1/math/catalan/:n",
"GET /v1/math/fibonacci/:n",
"GET /v1/math/gcd/:a/:b",
"GET /v1/math/is-prime/:n",
"GET /v1/math/next-prime/:n",
"GET /v1/math/previous-prime/:n",
"GET /v1/math/prime-pi/:n",
"GET /v1/math/pi/:n",
"GET /v1/math/prime-gap/:n",
"GET /v1/math/factor/:n",
"GET /v1/math/totient/:n",
"GET /v1/math/mobius/:n",
"GET /v1/math/divisor-count/:n",
"GET /v1/math/divisor-sum/:n",
"GET /v1/math/divisors/:n",
"GET /v1/catalan/:n",
"GET /v1/snapshot",
"GET /school/",
"GET /school/api/health",
"GET /school/api/tables",
"GET /school/api/tables/:table",
"GET /school/api/tables/:table/schema",
"GET /school/api/tables/:table/students/:student_code",
"GET /school/api/admin/tables/:table/students/:student_code",
"GET /school/api/admin/audit"
],
"build_profile": "release",
"environment": "production"
}
200 OK — Metadata returned500 Internal Server Error — Unexpected backend failureapp_version is taken from the package version at build time. build_profile identifies whether the binary was compiled with debug assertions. environment is an optional LAB_API_ENV deployment label, defaults to unknown, and must remain free of secrets.
curl -sS https://abhrankan.duckdns.org/api/health
{
"ok": true,
"service": "lab-api"
}
curl -sS http://127.0.0.1:8088/health
200 OKThis endpoint is intended to remain publicly readable for simple service checks and monitoring.
GET /v1/math/catalan/:n
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/catalan/0'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/catalan/10'
curl -sS 'https://abhrankan.duckdns.org/api/v1/math/catalan/34'
{
"n": 10,
"value": "16796"
}
The implementation computes Catalan numbers in u128 and therefore intentionally limits input to:
0 ≤ n ≤ 34
This is a deliberate product choice, not a general-purpose arbitrary-precision implementation.
The calculation uses the standard recurrence:
C(0) = 1
C(n) = C(n-1) * 2 * (2n - 1) / (n + 1)
curl -sS -i 'https://abhrankan.duckdns.org/api/v1/math/catalan/35'
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "n must be <= 34 for this demo (u128 limit)"
}
200 OK when 0 ≤ n ≤ 34400 Bad Request when n > 34GET /v1/snapshot
This endpoint provides a minimal system summary from the host running lab-api.
{
"hostname": "ip-172-31-77-184.ec2.internal",
"uptime": "up 2 days, 2 hours, 52 minutes",
"loadavg": "0.13 0.15 0.13",
"mem_available_kb": 580200
}
curl -sS -u 'username:password' https://abhrankan.duckdns.org/api/v1/snapshot
curl -sS -u 'username:password' http://127.0.0.1:8088/v1/snapshot
The snapshot endpoint is protected by Nginx HTTP Basic Authentication.
The Rust application itself does not enforce credentials for this route. Authentication is enforced at the public entry point before traffic reaches the backend.
If the client omits credentials or provides invalid credentials, the response is:
HTTP/1.1 401 Unauthorized
This is intentional. The endpoint exposes host-level information and is therefore treated as sensitive.
/api/* (public edge)GET /health — unauthenticatedGET /v1/info — unauthenticatedGET /v1/math/catalan/:n — unauthenticatedGET /v1/math/fibonacci/:n — unauthenticatedGET /v1/math/gcd/:a/:b — unauthenticatedGET /v1/math/is-prime/:n — unauthenticatedGET /v1/math/next-prime/:n — unauthenticatedGET /v1/math/previous-prime/:n — unauthenticatedGET /v1/math/prime-gap/:n — unauthenticatedGET /v1/math/prime-pi/:n — unauthenticatedGET /v1/math/pi/:n — unauthenticatedGET /v1/math/factor/:n — unauthenticatedGET /v1/math/totient/:n — unauthenticatedGET /v1/math/mobius/:n — unauthenticatedGET /v1/math/divisor-count/:n — unauthenticatedGET /v1/math/divisor-sum/:n — unauthenticatedGET /v1/math/divisors/:n — unauthenticatedGET /v1/catalan/:n — unauthenticatedGET /v1/snapshot — Nginx HTTP Basic Auth/school/* (public edge)GET /school/ and static assets — unauthenticatedGET /school/api/health — unauthenticatedGET /school/api/tables, table, schema, and privacy-filtered student routes — Nginx HTTP Basic AuthGET /school/api/admin/tables/... and GET /school/api/admin/audit — Nginx HTTP Basic Auth and username in LAB_API_ADMIN_USERS via X-Authenticated-UserFor /v1/snapshot, Nginx enforces HTTP Basic Authentication before proxying; the core Rust handler does not validate those credentials.
For School, Nginx authenticates only the protected /school/api/ API location and forwards $remote_user as X-Authenticated-User. The admin detail and audit handlers additionally check that value against LAB_API_ADMIN_USERS. The public UI and health route do not receive this authentication gate.
Do not expose port 8088 publicly: without Nginx, School routes would not have the edge Basic Auth gate.
# Public core API (no auth)
location /api/ {
proxy_pass http://127.0.0.1:8088/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Snapshot only
location = /api/v1/snapshot {
auth_basic "Private API";
auth_basic_user_file /etc/nginx/status.htpasswd;
proxy_pass http://127.0.0.1:8088/v1/snapshot;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Public School health
location = /school/api/health {
proxy_pass http://127.0.0.1:8088;
}
# Protected School API
location /school/api/ {
auth_basic "School Database";
auth_basic_user_file /etc/nginx/school.htpasswd;
proxy_pass http://127.0.0.1:8088;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Authenticated-User $remote_user;
}
# Public School UI and static assets
location /school/ {
proxy_pass http://127.0.0.1:8088;
}
Exact matches for /api/v1/snapshot take precedence over the /api/ prefix. Health, info, math, and the Catalan alias remain public.
https://abhrankan.duckdns.org/api/... → http://127.0.0.1:8088/...
https://abhrankan.duckdns.org/school/... → http://127.0.0.1:8088/school/...
Examples:
https://abhrankan.duckdns.org/api/health
→ http://127.0.0.1:8088/health
https://abhrankan.duckdns.org/api/v1/math/catalan/10
→ http://127.0.0.1:8088/v1/math/catalan/10
https://abhrankan.duckdns.org/api/v1/info
→ http://127.0.0.1:8088/v1/info
https://abhrankan.duckdns.org/school/api/tables/II_A/students/1001
→ http://127.0.0.1:8088/school/api/tables/II_A/students/1001
127.0.0.1:8088/api/v1/snapshot/school/ UI and /school/api/health/school/api/ routes/api/* serviceSCHOOL_DB_PATH, read-only at runtimecrypto-lab is a separate Rust/Axum service on 127.0.0.1:8089. Nginx
publishes it under /crypto-api/, leaves health, info, and math exploration public,
and protects hash/HMAC operations with HTTP Basic Authentication.
Repository: foxhackerzdevs/crypto-lab
Purpose: small, self-hosted service for SHA-2 hashing, HMAC generate/verify, and number-theoretic / elliptic curve cryptography exploration (FoxMath algorithms) for learning and lab experiments.
Non-goals (intentionally unsupported):
Warning: do not submit production private keys, passwords, API tokens, or other long-lived secrets. Examples use throwaway strings only.
https://abhrankan.duckdns.org/crypto-api/health
https://abhrankan.duckdns.org/crypto-api/v1/info
https://abhrankan.duckdns.org/crypto-api/v1/math/legendre
https://abhrankan.duckdns.org/crypto-api/v1/math/sqrtmod
https://abhrankan.duckdns.org/crypto-api/v1/math/factor
https://abhrankan.duckdns.org/crypto-api/v1/math/totient
https://abhrankan.duckdns.org/crypto-api/v1/math/order
https://abhrankan.duckdns.org/crypto-api/v1/math/dlog
https://abhrankan.duckdns.org/crypto-api/v1/math/mobius
https://abhrankan.duckdns.org/crypto-api/v1/math/sigma
https://abhrankan.duckdns.org/crypto-api/v1/math/tau
https://abhrankan.duckdns.org/crypto-api/v1/math/crt
https://abhrankan.duckdns.org/crypto-api/v1/math/cf
https://abhrankan.duckdns.org/crypto-api/v1/math/ecadd
https://abhrankan.duckdns.org/crypto-api/v1/math/ecmul
https://abhrankan.duckdns.org/crypto-api/v1/math/challenge
https://abhrankan.duckdns.org/crypto-api/v1/hash
https://abhrankan.duckdns.org/crypto-api/v1/hmac
https://abhrankan.duckdns.org/crypto-api/v1/hmac/verify
http://127.0.0.1:8089/health
http://127.0.0.1:8089/v1/info
http://127.0.0.1:8089/v1/math/legendre
http://127.0.0.1:8089/v1/math/sqrtmod
http://127.0.0.1:8089/v1/math/factor
http://127.0.0.1:8089/v1/math/totient
http://127.0.0.1:8089/v1/math/order
http://127.0.0.1:8089/v1/math/dlog
http://127.0.0.1:8089/v1/math/mobius
http://127.0.0.1:8089/v1/math/sigma
http://127.0.0.1:8089/v1/math/tau
http://127.0.0.1:8089/v1/math/crt
http://127.0.0.1:8089/v1/math/cf
http://127.0.0.1:8089/v1/math/ecadd
http://127.0.0.1:8089/v1/math/ecmul
http://127.0.0.1:8089/v1/math/challenge
http://127.0.0.1:8089/v1/hash
http://127.0.0.1:8089/v1/hmac
http://127.0.0.1:8089/v1/hmac/verify
| Route | Auth |
|---|---|
GET /crypto-api/health |
None |
GET /crypto-api/v1/info |
None |
GET, POST /crypto-api/v1/math/legendre |
None |
GET, POST /crypto-api/v1/math/sqrtmod |
None |
GET, POST /crypto-api/v1/math/factor |
None |
GET, POST /crypto-api/v1/math/totient |
None |
GET, POST /crypto-api/v1/math/order |
None |
GET, POST /crypto-api/v1/math/dlog |
None |
GET, POST /crypto-api/v1/math/mobius |
None |
GET, POST /crypto-api/v1/math/sigma |
None |
GET, POST /crypto-api/v1/math/tau |
None |
GET, POST /crypto-api/v1/math/crt |
None |
GET, POST /crypto-api/v1/math/cf |
None |
GET, POST /crypto-api/v1/math/ecadd |
None |
GET, POST /crypto-api/v1/math/ecmul |
None |
GET, POST /crypto-api/v1/math/challenge |
None |
POST /crypto-api/v1/hash |
Nginx Basic Auth |
POST /crypto-api/v1/hmac |
Nginx Basic Auth |
POST /crypto-api/v1/hmac/verify |
Nginx Basic Auth |
Example Nginx (htpasswd path is site-specific):
location = /crypto-api/health {
proxy_pass http://127.0.0.1:8089/health;
}
location = /crypto-api/v1/info {
proxy_pass http://127.0.0.1:8089/v1/info;
}
# Public math & number theory endpoints (FoxMath)
location /crypto-api/v1/math/ {
proxy_pass http://127.0.0.1:8089/v1/math/;
}
location /crypto-api/ {
auth_basic "crypto-lab";
auth_basic_user_file /etc/nginx/status.htpasswd;
proxy_pass http://127.0.0.1:8089/;
}
Exact health/info and math prefix matches keep those exploration endpoints public. Trailing slash on /crypto-api/ maps
/crypto-api/v1/hash → /v1/hash on the backend.
| Operation | Role |
|---|---|
| Hash | Digest of data only. Does not prove who sent the data. |
| HMAC | Tag over data under a shared key. Verifier needs the same key. |
| Verify | Recomputes the tag and compares in constant time. Wrong but well-formed MAC → 200 + "valid": false. Malformed hex → 400. |
| Legendre | Euler’s criterion $(a/p)$ over odd prime $p$. Returns $1$, $-1$, or $0$. |
| Jacobi | Evaluates the Jacobi symbol $(a/n)$ for odd positive $n$ via quadratic reciprocity in $O(\log n)$ without factoring. |
| Solovay–Strassen | Probabilistic primality test verifying Euler's criterion $a^{(n-1)/2} \equiv (a/n) \pmod n$ with error bound $\le 2^{-\text{rounds}}$. |
| SqrtMod | Tonelli-Shanks modular square roots modulo odd prime $p$. Returns positive roots. |
| CRT | Extended Euclidean GCD solver for simultaneous modular congruences. |
| CF | Continued fraction expansions and rational convergents for $num/den$. |
| EC Add / Mul | Weierstrass curve group addition and scalar multiplication ($k \cdot P$) over finite field $\mathbb{F}_p$. Built-in presets: toy97 and toy193. |
| Dlog | Discrete logarithm ($g^x \equiv h \pmod p$) via Baby-step Giant-step. |
| Arithmetic | Foundational multiplicative functions: Möbius $\mu(n)$, divisor sum $\sigma_k(n)$, divisor count $\tau(n)$ via individual routes or unified suite (/arith). |
| Challenge | Deterministic seeded problem generator for practice and self-study. |
curl -sS https://abhrankan.duckdns.org/crypto-api/v1/info
{
"service": "crypto-lab",
"api": "v1",
"version": "0.6.0",
"endpoints": [
"GET /health",
"GET /v1/info",
"POST /v1/hash",
"POST /v1/hmac",
"POST /v1/hmac/verify",
"GET /v1/math/legendre",
"POST /v1/math/legendre",
"GET /v1/math/jacobi",
"POST /v1/math/jacobi",
"GET /v1/math/solovay",
"POST /v1/math/solovay",
"GET /v1/math/sqrtmod",
"POST /v1/math/sqrtmod",
"GET /v1/math/factor",
"POST /v1/math/factor",
"GET /v1/math/totient",
"POST /v1/math/totient",
"GET /v1/math/order",
"POST /v1/math/order",
"GET /v1/math/dlog",
"POST /v1/math/dlog",
"GET /v1/math/mobius",
"POST /v1/math/mobius",
"GET /v1/math/sigma",
"POST /v1/math/sigma",
"GET /v1/math/tau",
"POST /v1/math/tau",
"GET /v1/math/arith",
"POST /v1/math/arith",
"GET /v1/math/crt",
"POST /v1/math/crt",
"GET /v1/math/cf",
"POST /v1/math/cf",
"GET /v1/math/ecadd",
"POST /v1/math/ecadd",
"GET /v1/math/ecmul",
"POST /v1/math/ecmul",
"GET /v1/math/challenge",
"POST /v1/math/challenge"
],
"build_profile": "release",
"environment": "production"
}
environment is an optional non-secret deployment label (e.g. LAB_API_ENV); default unknown.
curl -sS -u 'username:password' \
-H 'Content-Type: application/json' \
-d '{"algorithm":"sha256","data":"hello"}' \
https://abhrankan.duckdns.org/crypto-api/v1/hash
{
"algorithm": "sha256",
"digest": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
curl -sS -u 'username:password' \
-H 'Content-Type: application/json' \
-d '{"algorithm":"sha256","key":"secret","data":"message"}' \
https://abhrankan.duckdns.org/crypto-api/v1/hmac
{
"algorithm": "sha256",
"mac": "8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"
}
curl -sS -u 'username:password' \
-H 'Content-Type: application/json' \
-d '{"algorithm":"sha256","key":"secret","data":"message","mac":"8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"}' \
https://abhrankan.duckdns.org/crypto-api/v1/hmac/verify
{
"algorithm": "sha256",
"valid": true
}
All math endpoints accept either query parameters (GET) or a JSON payload (POST). No authentication is required for /crypto-api/v1/math/*.
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/legendre?a=10&p=13"
{"a": 10, "p": 13, "value": 1}
Evaluates the Jacobi symbol $(a/n)$ for odd positive integer $n$ via quadratic reciprocity without factoring:
GET /crypto-api/v1/math/jacobi?a={a}&n={n} or POST /crypto-api/v1/math/jacobia (integer, required): Numerator integer (can be positive, negative, or zero).n (integer, required): Modulus integer. Must be a positive odd integer ($n \ge 1$, $n \equiv 1 \pmod 2$).a (integer): Echo of input $a$.n (integer): Echo of modulus $n$.value (integer): Jacobi symbol $(a/n) \in \{1, -1, 0\}$ ($1$ indicates quadratic residue or pseudo-residue, $-1$ quadratic non-residue, $0$ indicates $\gcd(a, n) > 1$). Note: $(a/1) = 1$ for all $a$.400 Bad Request if $n < 1$, $n$ is even, or inputs are not valid 64-bit integers.# GET via query parameters
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/jacobi?a=10&n=21"
{"a": 10, "n": 21, "value": -1}
# POST via JSON payload
curl -sS -H "Content-Type: application/json" \
-d '{"a": 2, "n": 15}' \
https://abhrankan.duckdns.org/crypto-api/v1/math/jacobi
{"a": 2, "n": 15, "value": 1}
Probabilistic primality test verifying Euler's criterion $a^{(n-1)/2} \equiv (a/n) \pmod n$ for $k$ random bases $a$:
GET /crypto-api/v1/math/solovay?n={n}&rounds={k}&seed={s} or POST /crypto-api/v1/math/solovayn (integer, required): Candidate integer to test. Must be an integer $\ge 2$.rounds (integer, optional): Number of randomized bases to test, $1 \le \text{rounds} \le 100$ (default: 20, guaranteeing error probability $\le 2^{-20} \approx 9.5 \times 10^{-7}$).seed (integer, optional): Optional 64-bit unsigned integer seed for deterministic PRNG sequences.n (integer): Tested candidate.rounds (integer): Number of verification rounds executed.is_prime (boolean): true if candidate is probably prime; false if an Euler witness proved composite.method (string): Primality test identifier ("solovay_strassen").400 Bad Request if $n < 2$, rounds not in $[1, 100]$, or inputs are non-integers.# GET via query parameters (probable prime)
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/solovay?n=104729&rounds=20"
{"n": 104729, "rounds": 20, "is_prime": true, "method": "solovay_strassen"}
# POST via JSON payload (detects Carmichael composite)
curl -sS -H "Content-Type: application/json" \
-d '{"n": 561, "rounds": 20, "seed": 42}' \
https://abhrankan.duckdns.org/crypto-api/v1/math/solovay
{"n": 561, "rounds": 20, "is_prime": false, "method": "solovay_strassen"}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/sqrtmod?a=10&p=13"
{"a": 10, "p": 13, "roots": [6, 7]}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/factor?n=360"
{"n": 360, "factors": [{"prime": 2, "power": 3}, {"prime": 3, "power": 2}, {"prime": 5, "power": 1}], "prime_factors": [2, 3, 5], "expression": "2^3 * 3^2 * 5", "is_prime": false}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/totient?n=360"
{"n": 360, "phi": 96, "lambda": 12, "is_cyclic": false}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/order?a=3&m=7"
{"a": 3, "m": 7, "order": 6, "is_primitive_root": true}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/dlog?g=3&h=6&p=7"
{"g": 3, "h": 6, "p": 7, "x": 3, "order": 6}
Unified arithmetic suite (evaluates $\mu(n)$, $\sigma_k(n)$, and $\tau(n)$ in one request via a single factorization):
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/arith?n=12&k=1"
{"n": 12, "k": 1, "mobius": 0, "sigma": 28, "tau": 6}
Individual endpoints:
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/mobius?n=30"
{"n": 30, "value": -1}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/sigma?n=12&k=1"
{"n": 12, "k": 1, "value": 28}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/tau?n=12"
{"n": 12, "value": 6}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/crt?r=2,3,2&m=3,5,7"
{"remainders": [2, 3, 2], "moduli": [3, 5, 7], "x": 23, "modulus": 105}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/cf?num=355&den=113"
{"num": 355, "den": 113, "cf": [3, 7, 16], "convergents": [[3, 1], [22, 7], [355, 113]]}
curl -sS -H "Content-Type: application/json" \
-d '{"curve":"toy193","k":7}' \
https://abhrankan.duckdns.org/crypto-api/v1/math/ecmul
{"curve": "toy193", "k": 7, "point": [1, 59], "result": [66, 24], "result_on_curve": true}
curl -sS "https://abhrankan.duckdns.org/crypto-api/v1/math/challenge?topic=mixed&seed=42"
{"topic": "mixed", "seed": 42, "count": 1, "problems": [{"topic": "sqrtmod", "question": "Smaller positive root of x² ≡ 12 (mod 13)", "answer": 5}]}
sha256 and sha512Content-Type: application/json (math endpoints also support GET query strings)data, key, mac ≤ 32 KiB200 — success (including valid: false verification)400 — bad algorithm, malformed MAC hex, invalid query params, or math domain error (e.g., non-prime modulus, non-odd modulus in jacobi, candidate < 2 in solovay, quadratic non-residue in sqrtmod, zero denominator, negative scalar)401 — missing/invalid Basic Auth at Nginx for protected operations413 — body or field too large415 — non-JSON operation request404 — unknown route127.0.0.1:8089 onlyProtectSystem=strict, ProtectHome, PrivateTmp, namespace limits) as deployedThis service is a lab, not a secrets backend.