foss.global
Official codebase for the foss.global website, built with TypeScript and the @api.global ecosystem.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Prerequisites
- Node.js (>=20)
- pnpm 11
- Git CLI when the optional Git hosting runtime is enabled
- MongoDB connection for SmartData-backed public snapshots
MONGODB_URL/MONGODB_URI/MONGO_URLfor the MongoDB connection URL- Optional
MONGODB_DATABASE/MONGODB_DB/MONGO_DBoverride; defaults tofoss_global
Installation
pnpm install
Build
Compile TypeScript and bundle the website:
pnpm build
Watch
Rebuild on file changes:
pnpm watch
Start
Start the CLI web server:
pnpm start
Start using the TypeScript entrypoint:
pnpm run startTs
Usage Example
After starting the server, open your browser at http://localhost:3000 to view the site and API.
Optional Git Hosting Runtime
The Git runtime is disabled by default. When explicitly enabled, the same backend process serves:
- the website on port 3000;
- the CI V1 control plane on port 3001 when CI V1 is enabled;
- the isolated CI V2 control plane on its configured port when CI V2 is enabled;
- Git Smart HTTP and its administration API on port 3002;
- authenticated Git SSH on internal port 29419 when SSH is enabled, advertised externally on port 29460.
CI V2 and Git Smart HTTP both default to port 3002, so configure a distinct
CI_V2_CONTROL_PLANE_PORT before enabling them together.
Repository metadata, visibility, and native bundle descriptors are authoritative in SmartData. On startup, the runtime imports desired-public mirrors and restores every native repository from its SHA-256-addressed SmartBucket bundle. Authorized private mirrors hydrate on first access. Native repositories without a bundle are initialized as empty bare repositories. Partial metadata, a missing storage configuration, an unexpected object key, a size mismatch, or a digest mismatch fails startup closed. Live publication and revocation must use GitHostingRuntime.setRepositoryVisibility() so hydration, Smart HTTP access, public reads, and Codefeed change together; raw database visibility writes are unsupported.
Quota reconstruction accepts exactly 10,000 release repositories or package owners and rejects grouped or merged identity overflow before writes. Stored quota scans fail closed above 100,000 rows. Computed quotas are upserted before stale rows are removed with exact value fences, including stale nonzero rows, so reconstruction does not expose a temporary unmetered window.
Scheduled maintenance serializes with repository mutations, runs git fsck, restores current bundles into isolated disposable GitManager instances, compares every ref, reconciles reachable Git LFS pointers, and only then advances retention garbage collection. Bundle history, LFS objects, CI source archives, and CI caches are quota-accounted in SmartData; immutable shared objects use reference-aware mark/sweep collection with crash-recoverable deletion leases.
Mirror checkouts and restored repositories live only in a disposable process-local directory. Mirrors rebuild from their configured upstream URLs. Native repository durability comes only from SmartBucket objects plus SmartData descriptors. Smart HTTP and SSH pushes share one per-repository mutation lock, the same role authorization and immutable protected-ref policy, and the same durable snapshot, CI event, and audit path. The final receive-pack status is withheld until persistence succeeds. A snapshot failure stops the ingress and terminates the runtime so a supervisor restart restores the last durable state.
When native Git writes and CI are both enabled, each repository dispatch is pinned to the pushed commit's exact object ID. GitManager creates a bounded deterministic tar.gz source archive, the backend verifies its SHA-256 digest and stores it under a content-addressed SmartBucket key, and that immutable descriptor is persisted with the pending job before enqueueing it. Only the runner holding the job's current unexpired lease can download the archive. The runner authenticates only to the exact same-origin source endpoint and verifies the digest before extraction.
Configuration:
| Variable | Default | Purpose |
|---|---|---|
GIT_SMART_HTTP_ENABLED |
false |
Explicitly enables the runtime. |
GIT_SMART_HTTP_HOST |
127.0.0.1 |
Public service listener address behind the platform TLS route. |
GIT_SMART_HTTP_PORT |
3002 |
Git service port; it must not collide with the website or CI listener. |
GIT_SMART_HTTP_ALLOW_NON_LOOPBACK |
false |
Required with a non-loopback bind after protected TLS ingress exists. |
GIT_SMART_HTTP_PUBLIC_BASE_URL |
required when enabled | External HTTPS clone origin, for example https://code.foss.global. |
GIT_PUBLIC_BASE_URL |
https://foss.global |
External repository-page origin. |
GIT_SSH_PUBLIC_BASE_URL |
ssh://git@code.foss.global:29460 |
Advertised SSH clone origin. |
GIT_SSH_ENABLED |
false |
Enables the public-key-only SSH Git listener; Smart HTTP must also be enabled. |
GIT_SSH_HOST |
127.0.0.1 |
SSH listener address. Use a non-loopback address only with the explicit override. |
GIT_SSH_PORT |
29419 |
Raw SSH listener port; it must not collide with the website, CI, or Smart HTTP listener. |
GIT_SSH_ALLOW_NON_LOOPBACK |
false |
Required with a non-loopback SSH bind. |
GIT_SSH_HOST_KEY |
none | Required OpenSSH/PEM private host key. Treat it as a deployment secret and keep it stable across restarts. |
GIT_SSH_MAX_CONNECTIONS |
256 |
Maximum concurrent SSH connections. |
GIT_SSH_MAX_SESSIONS |
128 |
Maximum concurrent SSH sessions across all connections. |
GIT_MIRROR_MAX_REPOSITORIES |
250 |
Startup hydration limit. |
GIT_WRITE_ENABLED |
false |
Enables native repository administration and receive-pack. |
GIT_ADMIN_TOKEN |
none | Required 32–512 character bearer token when writes are enabled. |
GIT_MAX_PUSH_BYTES |
4294967296 |
Maximum streamed receive-pack request size. |
GIT_WEBHOOK_ENCRYPTION_KEY |
none | Optional 64-character hexadecimal AES-256 key that enables encrypted repository webhook secrets; writes must be enabled, and the key must remain stable across restarts. |
GIT_WEBHOOK_ALLOWED_HOSTS |
none | Required comma-separated exact DNS hostname allowlist when webhooks are enabled. Destinations must use HTTPS on port 443 and resolve only to public addresses. |
GIT_WEBHOOK_MAXIMUM_ATTEMPTS |
8 |
Maximum signed delivery attempts before a webhook delivery is marked failed. |
GIT_STORAGE_ENDPOINT |
none | S3-compatible hostname without credentials, a port, or a path; required when native repositories exist. |
GIT_STORAGE_PORT |
443 with SSL, otherwise 80 |
S3-compatible object-store port. |
GIT_STORAGE_USE_SSL |
true |
Selects HTTPS and must match an endpoint URL scheme when one is supplied. |
GIT_STORAGE_REGION |
us-east-1 |
Object-store region. |
GIT_STORAGE_ACCESS_KEY |
none | Required object-store access key. |
GIT_STORAGE_ACCESS_SECRET |
none | Required object-store secret. |
GIT_STORAGE_BUCKET |
none | Required S3 bucket for repository bundles and CI source archives; it is created when absent. |
GIT_STORAGE_MAX_BUNDLE_BYTES |
8589934592 |
Maximum bundle size accepted by GitManager and the storage verifier. |
GIT_STORAGE_MAX_BUNDLE_REPOSITORY_BYTES |
68719476736 |
Maximum retained current and historical bundle bytes per repository; capacity pressure evicts the oldest non-current bundle. |
GIT_STORAGE_BUNDLE_RETENTION_DAYS |
30 |
Minimum age before a non-current repository bundle becomes eligible for deletion. |
GIT_STORAGE_MAX_ARCHIVE_BYTES |
134217728 |
Maximum source archive file size accepted by GitManager and the storage verifier. |
GIT_STORAGE_MAX_SOURCE_REPOSITORY_BYTES |
21474836480 |
Maximum retained CI source archive bytes per repository. |
GIT_STORAGE_SOURCE_RETENTION_DAYS |
30 |
Minimum inactivity window before an unreferenced CI source archive becomes eligible for deletion. |
GIT_STORAGE_MAX_LFS_OBJECT_BYTES |
5368709120 |
Maximum size of one verified Git LFS object. |
GIT_STORAGE_MAX_LFS_REPOSITORY_BYTES |
107374182400 |
Maximum total reserved and retained Git LFS bytes per repository. |
GIT_STORAGE_MAX_RELEASE_ASSET_BYTES |
2147483648 |
Maximum size of one streamed release asset. |
GIT_STORAGE_MAX_RELEASE_REPOSITORY_BYTES |
53687091200 |
Maximum total reserved and retained release-asset bytes per repository. |
GIT_STORAGE_MAX_NPM_PACKAGE_BYTES |
134217728 |
Maximum decoded tarball size of one npm package version. |
GIT_STORAGE_MAX_NPM_OWNER_BYTES |
107374182400 |
Maximum total reserved and retained npm package bytes per organization owner. |
GIT_STORAGE_MAX_GENERIC_PACKAGE_FILE_BYTES |
2147483648 |
Maximum size of one streamed generic package file. |
GIT_STORAGE_MAX_GENERIC_PACKAGE_OWNER_BYTES |
107374182400 |
Maximum total reserved and retained generic package bytes per organization owner. |
GIT_STORAGE_MAX_OCI_BLOB_BYTES |
21474836480 |
Maximum size of one OCI image layer or configuration blob. |
GIT_STORAGE_MAX_OCI_MANIFEST_BYTES |
8388608 |
Maximum size of one exact OCI manifest or index. |
GIT_STORAGE_MAX_OCI_OWNER_BYTES |
536870912000 |
Maximum total reserved and retained OCI blob and manifest bytes per organization owner. |
GIT_STORAGE_LFS_RETENTION_DAYS |
30 |
Minimum age before repository LFS metadata unreachable from every ref becomes eligible for deletion. |
GIT_STORAGE_OBJECT_GC_GRACE_HOURS |
24 |
Mark/sweep grace period for unreferenced shared LFS objects. |
GIT_STORAGE_MAINTENANCE_INTERVAL_MINUTES |
60 |
Interval for bounded LFS reachability reconciliation and retention collection. |
GIT_STORAGE_INTEGRITY_CHECK_INTERVAL_HOURS |
24 |
Interval for full repository fsck and isolated bundle restore drills. |
CI_CACHE_OBJECT_GC_GRACE_HOURS |
24 |
Mark/sweep grace period for immutable CI cache objects no longer referenced by cache metadata or active uploads. |
CI_V2_ADMIN_TOKEN |
required | 32–8192 character bearer token for the isolated V2 administration routes. |
CI_V2_BOOTSTRAP_TOKEN |
required | One-time runner bootstrap bearer token; durable state stores only its keyed digest. |
CI_V2_BOOTSTRAP_TRUST_CLASS |
required | Trust class assigned to the bootstrapped runner and matched when jobs are claimed. |
CI_V2_BOOTSTRAP_ALLOWED_LABELS |
none | Comma-separated runner-label allowlist, with at most 32 unique protocol identifiers. |
CI_V2_CREDENTIAL_DIGEST_KEY |
required | 64-character hexadecimal root key for bootstrap/runner credential HMACs and an HKDF-separated enqueue-idempotency HMAC. Rotating it invalidates existing runner credentials and makes retained enqueue replays conflict. |
CI_V2_REPLAY_KEY_ID |
required | Persisted identifier for the current replay AES-256-GCM key. |
CI_V2_REPLAY_KEY |
required | Current 64-character hexadecimal replay encryption key. |
CI_V2_PREVIOUS_REPLAY_KEY_ID |
none | Previous replay key identifier; configure it together with the previous key while retained records reference it. |
CI_V2_PREVIOUS_REPLAY_KEY |
none | Previous 64-character hexadecimal replay encryption key. |
CI_V2_JOB_SECRET_KEY_ID |
required | Persisted identifier for the current job-secret AES-256-GCM key. |
CI_V2_JOB_SECRET_KEY |
required | Current 64-character hexadecimal job-secret encryption key. |
CI_V2_PREVIOUS_JOB_SECRET_KEY_ID |
none | Previous job-secret key identifier; configure it together with the previous key while active jobs reference it. |
CI_V2_PREVIOUS_JOB_SECRET_KEY |
none | Previous 64-character hexadecimal job-secret encryption key. |
CI_V2_CACHE_VALIDATION_EPOCH |
required | Durable cache-validation policy identifier; a configured/durable mismatch fails startup. |
CI_V2_CONTROL_PLANE_HOST |
127.0.0.1 |
Isolated V2 listener address. |
CI_V2_CONTROL_PLANE_PORT |
3002 |
Isolated V2 listener port; it must not collide with the website, CI V1, Git, or SSH listeners. |
CI_V2_CONTROL_PLANE_ALLOW_NON_LOOPBACK |
false |
Required with a non-loopback V2 bind after protected TLS ingress exists; enabling it also requires CI_V2_CONTROL_PLANE_PUBLIC_BASE_URL. |
CI_V2_CONTROL_PLANE_PUBLIC_BASE_URL |
none | Credential-free HTTPS origin required for every non-loopback V2 bind and routed directly to the V2 listener; paths are rooted at /api/runner/v2 and /api/ci/v2. |
CI_V2_SOURCE_RETENTION_DAYS |
14 |
Minimum inactivity window before an unreferenced V2 source archive is eligible for deletion. |
CI_V2_MAXIMUM_SOURCE_REPOSITORY_BYTES |
21474836480 |
Maximum retained V2 source archive bytes per repository. |
On Cloudly, the canonical object-storage binding variables (S3_ENDPOINT_HOST,
S3_ENDPOINT, S3_PORT, S3_USE_SSL, S3_REGION, access-key aliases,
secret-key aliases, and S3_BUCKET) provide the storage values automatically.
Explicit GIT_STORAGE_* values take precedence.
The CI control plane is enabled when any CI variable is configured. CI_RUNNER_SIGNING_SECRET, CI_JOB_ENCRYPTION_KEY, and CI_ADMIN_TOKEN are then required. CI_CONTROL_PLANE_PUBLIC_BASE_URL is additionally required when Git writes are enabled so jobs receive an externally reachable source URL. It must be the credential-free HTTPS origin routed to port 3001; runner credentials are never embedded in job specifications.
CI V2 remains absent only while every mapped CI_V2_* variable is unset or
empty. Any non-empty V2 value enters strict configuration validation, and all
values marked required above must then be present. It implements
foss-ci-runner/2.0-draft.2 on a separate socket and uses only
ffb_ci_v2_* SmartData collections and ci/v2/sources/ object keys. Enabling
it also requires GIT_SMART_HTTP_ENABLED=true and complete GIT_STORAGE_*
configuration. It does not move repository triggers away from CI V1.
The V2 runner surface provides discovery at GET /api/runner/v2, registration
at POST /api/runner/v2/runners/register, session creation and heartbeat under
/api/runner/v2/runners/:runnerId, claiming at POST /api/runner/v2/jobs/claim,
attempt accept, heartbeat, log, completion, and abandonment under
/api/runner/v2/jobs/:jobId/attempts/:attemptId, recovery at
POST /api/runner/v2/jobs/recover, and source download at
GET /api/runner/v2/sources/:sha256. Artifact and cache transfer paths are
present only to fail closed during the initial source-only activation.
The V2 admin surface provides readiness at GET /api/ci/v2/readiness, source
preparation at POST /api/ci/v2/sources, enqueue at POST /api/ci/v2/jobs, job
status and cancellation under /api/ci/v2/jobs/:jobId, bounded log reads at
GET /api/ci/v2/jobs/:jobId/logs, and runner drain control at
POST /api/ci/v2/runners/:runnerId/drain. Initial admission is restricted to
admin-prepared, admin-enqueued source-only jobs. Artifact and cache declarations
are rejected. Source archives are bounded to 8 GiB compressed, 64 GiB extracted
regular-file data, 1,000,000 entries, and 134,217,728 bytes of path metadata.
Source preparation accepts only active, non-archived native repositories.
Bootstrap and runner tokens are stored only as keyed HMAC-SHA256 digests.
Replay payloads and pending credential replacements use one AES-256-GCM key
set; job secrets use a separate key set. Keep previous keys configured while
readiness reports retained references to them. Changing
CI_V2_CACHE_VALIDATION_EPOCH requires an explicit data migration rather than
an environment-only change. Terminal jobs, attempts, logs, released source
references, invalidated sessions, and audit records are retained for 30 days
and then removed by bounded background cleanup. Enqueue idempotency keys are
unique within a tenant, so independent tenants may safely use the same key.
Readiness fails closed when the control-plane lifecycle, database, reaper timer,
reaper freshness, retained replay keys, or active job-secret keys are unhealthy.
CI V2 JSON requests and responses are limited to 2 MiB. One UTF-8 log chunk is limited to 64 KiB and one attempt to 1 GiB, with lower limits applied when negotiated by the job. Admin log reads require attemptId, default fromSequence to zero, and return at most five chunks. Oversized request bodies, log writes, and JSON responses fail with HTTP 413.
Claim selection scans queued jobs in deterministic order, fails closed above 100,000 examined jobs or a 30-second deadline, and serializes claim admission with runner drain updates. After cancellation is accepted, completion must use status canceled and the exact cancellation request ID; cancellation deadlines fence acceptance, heartbeat, new logs, completion, and new source-download admission. Concurrent process-exit paths share one shutdown operation and wait for the same dependency cleanup. Request disconnects cancel source preparation and downloads; shutdown cancels and drains lifecycle-owned source preparation, downloads, and reaping before dependencies close. These paths propagate cancellation through @foss.global/gitmanager 1.7.0 and @push.rocks/smartbucket 4.13.0 object existence, removal, upload, verification, and download operations. Once the atomic source ready transition succeeds, publication wins over a later cancellation and the prepared source remains available.
These cancellation contracts do not activate CI V2. Activation still requires durable cross-replica runner-session and credential revocation, or an enforceable single-replica invariant, in addition to the Git Smart HTTP and storage prerequisites above.
CI V2 does not adopt V1 sources, caches, credentials, or retention ownership. Before removing V1, stop V1 trigger admission, drain or intentionally terminate active jobs, explicitly retain or migrate history and artifacts, rotate runner and admin credentials, verify V2 readiness, storage, source delivery, and rollback, and only then remove V1 routes, collections, credentials, and object retention ownership.
Write administration uses Authorization: Bearer $GIT_ADMIN_TOKEN with JSON request bodies:
GET /api/v1/admin/operationsreturns a no-store operational snapshot covering repository visibility, retained bundle/LFS/source/release/npm/generic/OCI-package bytes, CI runners/jobs/artifacts/caches, dispatch backlogs, merge and webhook failures, configured storage limits, alerts, and the latest 20 maintenance runs;POST /api/v1/repositoriescreates an empty native repository;POST /api/v1/userscreates a Git user;PUT /api/v1/users/:username/statusenables or disables a user;POST /api/v1/users/:username/tokensissues a user access token whose plaintext is returned once;DELETE /api/v1/users/:username/tokens/:tokenIdrevokes a user access token;POST /api/v1/users/:username/ssh-keysregisters an Ed25519, ECDSA, or RSA public key;GET /api/v1/users/:username/ssh-keyslists key metadata and fingerprints without returning key material;DELETE /api/v1/users/:username/ssh-keys/:keyIdrevokes a key;PUT /api/v1/organizations/:org/members/:usernameassigns an owner, maintainer, developer, or reader role;DELETE /api/v1/organizations/:org/members/:usernameremoves membership;POST /api/v1/repositories/:org/:repo/tokensissues a scoped repository token whose plaintext is returned once;DELETE /api/v1/repositories/:org/:repo/tokens/:tokenIdrevokes a token;PUT /api/v1/repositories/:org/:repo/visibilitypublishes or privatizes a repository;GET|POST /api/v1/repositories/:org/:repo/releaseslists or creates native releases for existing Git tags;PUT|DELETE /api/v1/repositories/:org/:repo/releases/:tagupdates or deletes a native release;POST /api/v1/repositories/:org/:repo/releases/:tag/assets/:namestreams a fixed-length release asset into integrity-verified object storage;DELETE /api/v1/repositories/:org/:repo/releases/:tag/assets/:assetIddeletes a release asset;GET|POST /api/v1/repositories/:org/:repo/webhookslists or creates signed repository webhooks;DELETE /api/v1/repositories/:org/:repo/webhooks/:webhookIddeletes a webhook and fails its pending deliveries;GET /api/v1/repositories/:org/:repo/webhook-deliverieslists the latest durable delivery records;POST /api/v1/repositories/:org/:repo/webhook-deliveries/:deliveryId/redeliverexplicitly retries a completed or failed delivery.
Operational state and visibility totals use bounded SmartData grouped pages. Independent collections are queried in parallel rather than in one cross-collection transaction, so the result is operationally coherent but not a transactionally point-in-time snapshot. Queries request a two-second MongoDB limit, and projected dispatch/quota scans fail closed beyond 100,000 documents.
Published native releases replace the tag-derived public release for the same tag while preserving other tag releases. Assets download from GET /releases/download/:org/:repo/:releaseId/:assetId/:name; access follows repository visibility, and responses include the verified SHA-256 digest. Draft releases and their assets are never exposed publicly. Uploads use staged promotion, independent size and digest verification, atomic per-repository accounting, and restart recovery without filesystem persistence.
Webhook request bodies are stable JSON. Each request includes X-Foss-Event, a unique X-Foss-Delivery, and X-Foss-Signature-256: sha256=<hex HMAC>. The configured secret is encrypted at rest and is never returned by the administration API. Push deliveries are persisted before Smart HTTP or SSH reports a successful push; delivery itself runs from a leased, bounded-retry outbox.
Token issuance accepts one or both of the read and write permissions. Omitting permissions preserves the read/write default:
{"label":"deployment","permissions":["read"]}
Repository tokens are stored only as SHA-256 hashes. Public repositories clone anonymously. Private clone/fetch requires read; pushes require write. The token is accepted as a Basic-auth password or bearer token:
git remote add origin https://code.foss.global/org/repo.git
git fetch origin
git push origin main
For Basic authentication, use git as the username and the repository token as the password through an appropriate Git credential helper.
The same code.foss.global listener exposes a Gitea-compatible npm registry at /api/packages/:owner/npm/. Public package metadata and tarballs are readable anonymously. Private reads require organization read permission; publishing, dist-tag mutations, and unpublishing require write. Use a user access token issued above for a user who belongs to the target organization:
@foss.global:registry=https://code.foss.global/api/packages/foss.global/npm/
//code.foss.global/api/packages/foss.global/npm/:_authToken=${FOSS_GLOBAL_TOKEN}
always-auth=true
The registry supports scoped and unscoped packages with pnpm publish, pnpm install, pnpm view, pnpm search, pnpm dist-tag, and pnpm unpublish. For example:
pnpm publish --access public
pnpm add @foss.global/example
pnpm dist-tag add @foss.global/example@1.0.0 stable
pnpm unpublish @foss.global/example@1.0.0 --force
Published tarballs are staged, independently verified against SHA-1 and SHA-512 metadata, promoted to object storage, and atomically charged to the organization quota. Package metadata, versions, visibility, tags, and quotas live in SmartData; interrupted uploads and deletions recover without filesystem persistence.
The same listener also implements the Gitea generic package protocol at /api/packages/:owner/generic/:package/:version/:filename. Upload and delete operations accept a user token as a Basic-auth password or bearer token and require organization write permission. Public files download anonymously; private packages require read. New packages are public unless the first upload sends X-Package-Visibility: private:
curl --user "publisher:${FOSS_GLOBAL_TOKEN}" \
--upload-file ./artifact.tar.gz \
https://code.foss.global/api/packages/foss.global/generic/example/1.0.0/artifact.tar.gz
curl --fail --output artifact.tar.gz \
https://code.foss.global/api/packages/foss.global/generic/example/1.0.0/artifact.tar.gz
curl --user "publisher:${FOSS_GLOBAL_TOKEN}" --request DELETE \
https://code.foss.global/api/packages/foss.global/generic/example/1.0.0
Uploads require Content-Length, reject transfer/content encodings, and support X-Checksum-Sha256: <hex> or Digest: sha-256=<base64> verification. Files are immutable within a package version, staged and independently verified in object storage, atomically quota-accounted, and recovered after interrupted uploads or deletions. Package/file metadata and quotas live in SmartData; file payloads live in SmartBucket.
The same listener is also an OCI Distribution registry under /v2/. Repository names use {owner}/{image} and may contain additional lowercase path segments. A user access token for a member of the owner organization is accepted as the Docker password:
docker login code.foss.global --username publisher
docker tag example:1.0.0 code.foss.global/foss.global/example:1.0.0
docker push code.foss.global/foss.global/example:1.0.0
docker pull code.foss.global/foss.global/example:1.0.0
The registry implements resumable and monolithic blob uploads, same-owner cross-repository mounts, blob and manifest pull/HEAD/delete, case-insensitive tags, paginated tag listing, OCI referrers with artifact-type filtering, and Docker/OCI image manifests and indexes. New repositories are public by default. An authenticated manifest PUT can set X-Package-Visibility: private; private pulls require organization read permission and all pushes/deletes require write.
Blob chunks, final blobs, and exact manifest bytes are staged and independently SHA-256 verified in SmartBucket. Repository metadata, content links, upload sessions, tags, visibility, and owner quota reservations live in SmartData. OCI quota admission uses one atomic numeric expression over retained, reserved, and requested bytes, so concurrent reservations cannot exceed the owner budget or fail from retry exhaustion. Startup and scheduled maintenance recover abandoned uploads and crash-interrupted deletions without filesystem persistence. Docker push and pull interoperability is covered by the Mongo-backed integration suite.
The website exposes first-class package discovery and management at https://foss.global/#/packages. Anonymous discovery includes public npm, generic, and OCI packages across owners. Supplying an organization filter and user access token also includes that owner's private packages. Package detail views show versions, files or image tags, retained storage, exact digests, and ready-to-copy client commands. Organization writers can change visibility and delete npm versions, generic files, or OCI tags. The browser sends tokens only in same-origin typed requests and keeps them in component memory rather than filesystem or browser persistence.
SSH accepts only the git username and registered public keys. Public repositories require a valid active user key; private reads require at least the organization reader role, and pushes require at least developer. Shells, PTYs, forwarding, agents, subsystems, and arbitrary commands are rejected. Register the user's OpenSSH public key, then use the advertised origin:
git remote add ssh-origin ssh://git@code.foss.global:29460/org/repo.git
git fetch ssh-origin
git push ssh-origin main
Production must route code.foss.global through the platform TLS ingress to port 3002 and forward public raw TCP port 29460 to the same backend's internal port 29419 when SSH is enabled. Public TCP port 29419 belongs to the Cloudron instance and must not be claimed by this workload. The Rust Git transport binds only to an operating-system-assigned loopback port and accepts private upload-pack and receive-pack solely from the per-process ingress token; it is never the public listener. Keep write mode disabled until object-store credentials are trusted and the exact production image has passed HTTP and SSH clone, push, snapshot, restart, and restore smoke tests.
The source-controlled Cloudly declaration models this as one service with container ports 3000, 3001, 3002, and 29419. foss.global targets port 3000 and code.foss.global targets port 3002; both HTTP routes verify /healthz. Port 3001 is the CI V1 runner endpoint and must be exposed through the port-targeted TLS origin configured by CI_CONTROL_PLANE_PUBLIC_BASE_URL. The current declaration does not expose a separate CI V2 listener; enabling V2 in a deployment requires assigning and declaring a distinct container port and routing its credential-free HTTPS origin directly to that port. Internal port 29419 is raw SSH; DcRouter must expose it as public TCP 29460 for this same backend rather than introducing another repository or service.
Testing
Run the automated test suite:
pnpm test
The default suite includes disposable-Mongo CI V2, migration, catalog, and snapshot tests. Additional full Git/package lifecycle tests are enabled through CI_TEST_MONGODB_URL; each creates and removes a unique database and temporary resources:
CI_TEST_MONGODB_URL=mongodb://127.0.0.1:27017 pnpm test
Durable SSH push coverage additionally uses CI_TEST_S3_ENDPOINT, CI_TEST_S3_PORT, CI_TEST_S3_ACCESS_KEY, and CI_TEST_S3_ACCESS_SECRET. It creates and removes a unique database, bucket, repository, and temporary SSH keys.
Project Structure
./
├── ts/ # CLI source files
├── ts_web/ # Frontend source files
├── html/ # Static HTML templates
├── assets/ # Static assets (images, styles, etc.)
├── dist_ts/ # Compiled CLI output
├── dist_serve/ # Bundled website for serving
├── cli.js # Node.js CLI entrypoint
├── cli.ts.js # TypeScript CLI entrypoint
└── readme.md # Project README
Module/Plugin Import Guidelines
CLI code imports modules via a central plugin file:
import * as plugins from './ffb.plugins.js';
Web code generally imports directly from packages or uses ts_web/plugins.ts when applicable.
Contributing
Contributions are welcome! To contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/YourFeature) - Commit your changes (
git commit -m "Add awesome feature") - Push to your fork (
git push origin feature/YourFeature) - Open a pull request against the
mainbranch
Please follow the existing code style and run pnpm build before submitting.
License
This project is UNLICENSED. See package.json for details.
Contact / Support
- Repository: https://code.foss.global/losslessone_websites/foss.global
- Issues and security reports: https://community.foss.global/