{"id":19390774,"url":"https://github.com/einstore/einstore","last_synced_at":"2026-01-24T20:17:57.738Z","repository":{"id":74256416,"uuid":"74880952","full_name":"Einstore/Einstore","owner":"Einstore","description":"Enterprise/development appstore for easy app deployment, completely open sourced","archived":false,"fork":false,"pushed_at":"2026-01-18T18:55:09.000Z","size":69136,"stargazers_count":134,"open_issues_count":42,"forks_count":19,"subscribers_count":17,"default_branch":"main","last_synced_at":"2026-01-19T02:47:56.834Z","etag":null,"topics":["appstore","einstore","enterprise","enterprise-appstore","mdm"],"latest_commit_sha":null,"homepage":"https://einstore.pro","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Einstore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2016-11-27T10:31:01.000Z","updated_at":"2026-01-18T18:55:13.000Z","dependencies_parsed_at":"2023-05-20T14:15:10.307Z","dependency_job_id":null,"html_url":"https://github.com/Einstore/Einstore","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Einstore/Einstore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Einstore%2FEinstore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Einstore%2FEinstore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Einstore%2FEinstore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Einstore%2FEinstore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Einstore","download_url":"https://codeload.github.com/Einstore/Einstore/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Einstore%2FEinstore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28736503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T19:23:36.361Z","status":"ssl_error","status_checked_at":"2026-01-24T19:23:28.966Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appstore","einstore","enterprise","enterprise-appstore","mdm"],"created_at":"2024-11-10T10:23:16.359Z","updated_at":"2026-01-24T20:17:57.732Z","avatar_url":"https://github.com/Einstore.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Einstore (Refactor)\n\nModern private app distribution platform for iOS + Android.\n\n## Structure\n- `API/` Node.js + Fastify + Prisma + Postgres\n- `Admin/` React + Tailwind (CSR)\n- `Web/` React + Tailwind (CSR)\n- `frameworks/` Mobile tracking libraries (iOS, Android, Flutter)\n- `Libraries/` External submodules (auth only)\n- `dev/` Legacy reference (gitignored)\n\n## Notes\n- Auth is provided by the external `rafiki270/auth` submodule.\n- This is a ground-up refactor; legacy code is reference-only.\n\n## Run (local)\n- `make launch` starts the API, Admin, local ingest function, and MinIO (persistent). Use your existing local Caddy (outside this repo) for domain routing.\n- Add `/etc/hosts` entries pointing `admin.local.einstore.pro`, `api.local.einstore.pro`, and `local.einstore.pro` to `127.0.0.1`, then proxy those hosts to `localhost:8101` and `localhost:8100` in your own Caddy config.\n- For App Platform build/run simulation, see `docs/app-platform-local-sim.md`.\n- Default ports now live in the 8100 range: API 8100, Admin 8101, Postgres 8102 (Docker host), and the test runner on 8103.\n\n## Uploading a binary to Einstore\n1. Create or copy an API key in Admin (`API Keys`). The API key identifies the team.\n2. Request a presigned URL from `/ingest/upload-url` (API key can be passed via `token` query param):\n   ```bash\n   curl -X POST \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"filename\":\"binary.apk\",\"sizeBytes\":12345678}' \\\n     \"https://api.local.einstore.pro/ingest/upload-url?token=YOUR_API_KEY_TOKEN\"\n   ```\n3. Upload the binary directly to storage using the returned `uploadUrl`, honoring the `headers` payload if provided.\n4. Finalize the upload with `/ingest/complete-upload` (send `key` from step 2).\n5. Teams have a storage quota (default 1 GB). If an upload would exceed the quota, the API purges the oldest builds first (never deletes the last build of an app). If no space can be freed, the upload is rejected with a clear error.\n\n## Storage CORS (self-hosting)\nWhen self-hosting, browser uploads PUT directly to your S3-compatible bucket using presigned URLs. Browsers send a CORS preflight request; if your bucket does not allow the Admin origin, the PUT fails with a network/CORS error before the file ever reaches storage. You must configure bucket CORS for Admin (and any other upload origins).\n\nExample CORS (S3/Spaces/MinIO):\n```json\n{\n  \"CORSRules\": [\n    {\n      \"AllowedOrigins\": [\n        \"https://admin.your-domain.com\",\n        \"https://api.your-domain.com\",\n        \"http://localhost:8101\"\n      ],\n      \"AllowedMethods\": [\"GET\", \"PUT\", \"POST\", \"HEAD\", \"DELETE\"],\n      \"AllowedHeaders\": [\"*\"],\n      \"ExposeHeaders\": [\"ETag\", \"x-amz-request-id\", \"x-amz-id-2\", \"x-amz-version-id\"],\n      \"MaxAgeSeconds\": 3000\n    }\n  ]\n}\n```\n\nDigitalOcean Spaces (CLI example):\n```bash\naws --endpoint-url https://lon1.digitaloceanspaces.com s3api put-bucket-cors \\\n  --bucket your-bucket \\\n  --cors-configuration file://cors.json\n```\n\nNotes:\n- The presigned URL is generated with `SignedHeaders=host` only. Do not add custom headers (including `Content-Type`) unless the API explicitly returns them, or the signature will be invalid.\n- If you change the Admin or API domain, update your bucket CORS to match the new origins.\n\n## Integration tests (Newman)\n- Requirements: API running locally, valid `accessToken`, `teamId`, `apiKeyToken`, and sample APK/IPA paths.\n- Update `tests/newman/local.postman_environment.json` with your tokens and file paths.\n- Run: `cd API \u0026\u0026 npm run test:integration`\n- The command executes the Postman collection folders: `Ingest Upload (APK)` and `Ingest Upload (IPA)` against `postman_collection.json`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feinstore%2Feinstore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feinstore%2Feinstore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feinstore%2Feinstore/lists"}