{"id":49550770,"url":"https://github.com/zademy/nekolu","last_synced_at":"2026-05-02T22:08:54.917Z","repository":{"id":349252107,"uuid":"1201625614","full_name":"zademy/nekolu","owner":"zademy","description":"Nekolu is a Spring Boot 4 application that turns Telegram into a personal file workspace. It uses the native TDLib Java bindings to upload, browse, download, preview, organize, and inspect files stored in Saved Messages and Telegram-backed folder channels.","archived":false,"fork":false,"pushed_at":"2026-04-25T00:33:55.000Z","size":13804,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-25T02:36:21.248Z","etag":null,"topics":["caffeine","file-management","java","rest-api","self-hosted","spring-boot","sse","tdlib","telegram","telegram-client","thymeleaf"],"latest_commit_sha":null,"homepage":"https://nekolu.me","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zademy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.adoc","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-04T23:44:36.000Z","updated_at":"2026-04-25T00:33:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zademy/nekolu","commit_stats":null,"previous_names":["zademy/nekolu"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zademy/nekolu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zademy%2Fnekolu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zademy%2Fnekolu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zademy%2Fnekolu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zademy%2Fnekolu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zademy","download_url":"https://codeload.github.com/zademy/nekolu/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zademy%2Fnekolu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32550993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T21:31:48.061Z","status":"ssl_error","status_checked_at":"2026-05-02T21:31:46.574Z","response_time":132,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["caffeine","file-management","java","rest-api","self-hosted","spring-boot","sse","tdlib","telegram","telegram-client","thymeleaf"],"created_at":"2026-05-02T22:08:50.727Z","updated_at":"2026-05-02T22:08:54.909Z","avatar_url":"https://github.com/zademy.png","language":"Java","funding_links":["https://ko-fi.com/C0C01Y1SQI"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eNekolu\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Telegram-backed personal file workspace built with Spring Boot 4, Java 25, and TDLib.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://nekolu.me/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Official%20Site-nekolu.me-0f172a?style=for-the-badge\u0026logo=googlechrome\u0026logoColor=white\" alt=\"Official site\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"./LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge\" alt=\"MIT License\"\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Java-25-f89820?style=for-the-badge\u0026logo=openjdk\u0026logoColor=white\" alt=\"Java 25\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Spring%20Boot-4-6db33f?style=for-the-badge\u0026logo=springboot\u0026logoColor=white\" alt=\"Spring Boot 4\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TDLib-Native%20Java-229ed9?style=for-the-badge\u0026logo=telegram\u0026logoColor=white\" alt=\"TDLib Native Java\"\u003e\n  \u003ca href=\"https://deepwiki.com/zademy/nekolu\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/DeepWiki-Ask%20AI-5b21b6?style=for-the-badge\u0026logo=googledocs\u0026logoColor=white\" alt=\"Ask DeepWiki\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://nekolu.me/\"\u003eOfficial Website\u003c/a\u003e\n  ·\n  \u003ca href=\"http://localhost:8080/swagger-ui.html\"\u003eSwagger UI\u003c/a\u003e\n  ·\n  \u003ca href=\"http://localhost:8080/v3/api-docs\"\u003eOpenAPI\u003c/a\u003e\n\u003c/p\u003e\n\nNekolu is a Spring Boot 4 application that turns Telegram into a personal file workspace.\nIt uses the native TDLib Java bindings to upload, browse, download, preview, organize, and inspect files stored in **Saved Messages** and Telegram-backed folder channels.\n\nThe project exposes:\n\n- a REST API documented with OpenAPI / Swagger UI\n- a server-rendered Thymeleaf interface with i18n support\n- TDLib-backed file discovery, upload, download, and inline preview workflows\n- logical drive features such as virtual paths, archive state, and trash operations\n- folder management backed by private Telegram channels\n- real-time download progress via WebSocket\n- observability through Actuator, Micrometer metrics, and Prometheus\n\n---\n\n## Core capabilities\n\n### File management\n- List files with filters, sorting, pagination, and folder scoping\n- Upload documents and photos to Telegram\n- Download files on demand\n- View downloaded media inline in the browser\n- Generate thumbnails and stream metadata\n- Run batch downloads and inspect progress\n\n### Logical drive features\n- Assign virtual paths to files\n- Archive and restore files\n- Move files between logical paths\n- Send files to logical trash and restore them later\n- Keep logical metadata alongside raw Telegram metadata\n\n### Folder management\n- Create folders backed by private Telegram channels\n- List folders\n- Delete folders\n- List files inside a specific folder channel\n\n### Observability\n- OpenAPI / Swagger UI documentation\n- Spring Boot Actuator with custom TDLib + disk health indicators\n- Micrometer metrics with Prometheus exporter (downloads, uploads, folders, errors)\n- Caffeine caches for file metadata\n- Structured logging with request correlation IDs (MDC)\n- TDLib-backed statistics for files, storage, and network activity\n\n### Resilience\n- Global exception handler with consistent error responses\n- Bean Validation on all request DTOs\n- Semaphore-based rate limiter for TDLib operations\n- Centralized TDLib precondition checks\n\n---\n\n## Technology stack\n\n| Layer | Technology |\n|-------|-----------|\n| Language | Java 25 |\n| Framework | Spring Boot 4.0.5 |\n| Web | Spring MVC + Thymeleaf + Layout Dialect |\n| Telegram | TDLib native Java bindings (`org.drinkless:tdlib`) |\n| API Docs | SpringDoc OpenAPI 3.0 |\n| Validation | Jakarta Bean Validation |\n| Caching | Caffeine |\n| Metrics | Micrometer + Prometheus |\n| Real-time | WebSocket |\n| Logging | Logback with JSON output (prod) + MDC correlation |\n| i18n | Spring MessageSource (English, Spanish) |\n| Build | Maven |\n| Container | Docker + Docker Compose |\n| Frontend | Vanilla JavaScript + Tailwind CSS 4 |\n\n---\n\n## Runtime requirements\n\n- Java 25\n- Maven 3.9+\n- A Telegram application created at [my.telegram.org](https://my.telegram.org)\n- A valid TDLib runtime environment with the native library available\n- A previously authenticated TDLib session in the configured `tdlib/` directory\n\n### Important TDLib constraint\n\nTDLib does **not** allow multiple processes to use the same database directory at the same time. Do not run the TDLib example client and Nekolu simultaneously against the same directory, and do not start two Nekolu instances sharing the same TDLib database.\n\n---\n\n## Getting started\n\n### 1. Create Telegram API credentials\n\nGo to [my.telegram.org/apps](https://my.telegram.org/apps) and create an application. You will need:\n\n- your `api_id`\n- your `api_hash`\n- your Telegram numeric user ID\n\nTo find your numeric user ID, you can use Telegram Desktop, a TDLib script calling `getMe`, or a trusted Telegram utility.\n\n### 2. Configure the application\n\nCopy the example properties file and fill in your values:\n\n```bash\ncp src/main/resources/application.example.properties src/main/resources/application.properties\n```\n\nUse environment variables to provide sensitive values instead of hardcoding them:\n\n```bash\nexport TELEGRAM_API_ID=\u003cyour_api_id\u003e\nexport TELEGRAM_API_HASH=\u003cyour_api_hash\u003e\nexport TELEGRAM_USER_ID=\u003cyour_numeric_user_id\u003e\n```\n\n\u003e **Never commit `application.properties` with real credentials to version control.**\n\n### 3. Authenticate TDLib (first run only)\n\nNekolu does **not** provide its own Telegram login flow. It expects an already authorized TDLib session in the configured `tdlib/` directory.\n\nFrom the project root, run:\n\n```bash\njava --enable-native-access=ALL-UNNAMED -Djava.library.path=lib -cp lib/tdlib.jar org.drinkless.tdlib.example.Example\n```\n\nFollow the interactive prompts to complete authentication (phone number, verification code, and 2FA password if enabled). After successful login, quit the example client before starting Nekolu.\n\n### 4. Run the application\n\n**With Maven:**\n\n```bash\nmvn spring-boot:run\n```\n\n**As a packaged JAR:**\n\n```bash\nmvn clean package -DskipTests\njava -jar target/nekolu-1.0.0.jar\n```\n\n**With Docker Compose:**\n\n```bash\ndocker compose up --build\n```\n\nThe application starts at [http://localhost:8080](http://localhost:8080).\n\n---\n\n## Configuration reference\n\nThe main configuration file is `src/main/resources/application.properties`. See `application.example.properties` for all available options.\n\n### Key configuration areas\n\n| Area | Properties prefix | Description |\n|------|-------------------|-------------|\n| Telegram credentials | `telegram.api.*`, `telegram.user.id` | API ID, hash, and user ID (use env vars) |\n| TDLib directories | `telegram.database-directory`, `telegram.files-directory` | Session state, file cache, SQLite metadata |\n| TDLib features | `telegram.use-*` | Feature flags for message/chat/file databases |\n| Upload limits | `spring.servlet.multipart.*` | Max file and request sizes (default: 2 GB) |\n| Async timeout | `spring.mvc.async.request-timeout` | Timeout for async MVC requests (default: 5 min) |\n| Rate limiter | `nekolu.rate-limit.*` | Max concurrent TDLib operations and acquire timeout |\n| Actuator | `management.endpoints.*` | Exposed health, metrics, and Prometheus endpoints |\n\n---\n\n## Docker\n\nThe project includes a multi-stage `Dockerfile` and a `docker-compose.yml`.\n\n```bash\ndocker compose up --build\n```\n\nEnvironment variables for Telegram credentials are passed through the compose file. TDLib session data is persisted in a named volume (`tdlib-data`).\n\n\u003e **Note:** The Dockerfile expects a Linux-compatible TDLib native library (`libtdjni.so`) in the `libs/` directory. Adjust the path if your setup differs.\n\n---\n\n## API documentation\n\nOnce running, the API documentation is available at:\n\n- **Swagger UI:** [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html)\n- **OpenAPI JSON:** [http://localhost:8080/v3/api-docs](http://localhost:8080/v3/api-docs)\n- **Developer reference:** `documentation.md`\n\n### API areas\n\n| Area | Base path | Capabilities |\n|------|-----------|-------------|\n| Files | `/api/telegram/files` | List, download, upload, preview, thumbnails, batch operations, archive, move, trash, delete, export |\n| Folders | `/api/telegram` | Create, list, delete folder channels |\n| Progress | `/ws/download-progress` | WebSocket endpoint for real-time download progress |\n| Health | `/actuator/health` | TDLib authorization, disk space, directory accessibility |\n| Metrics | `/actuator/prometheus` | Prometheus-compatible metrics endpoint |\n\n\u003e The OpenAPI definition is the source of truth for request and response contracts.\n\n---\n\n## Web interface\n\nThe Thymeleaf UI includes the following screens:\n\n- **Dashboard** — overview and quick actions\n- **Files** — file listing, search, upload, and inline preview\n- **Folders** — folder management\n- **Folder Files** — files inside a specific folder channel\n- **Statistics** — TDLib storage, network, and account limits\n\n### UI behavior notes\n\n- File tables and grids are backed by the REST API\n- Inline media preview is available only when a file is fully downloaded locally\n- Uploads use a staging directory under `tdlib/upload-staging`\n- The UI supports language switching via the `lang` query parameter (e.g., `?lang=es`)\n\n---\n\n## Internationalization (i18n)\n\nNekolu supports multiple languages through Spring MessageSource:\n\n| Locale | File |\n|--------|------|\n| English (default) | `messages.properties` |\n| Spanish | `messages_es.properties` |\n\nThe locale is resolved from the `Accept-Language` header or the `lang` query parameter.\n\n---\n\n## Project structure\n\n```text\nsrc/main/java/com/zademy/nekolu/\n├── Application.java\n├── config/\n│   ├── CacheConfig.java\n│   ├── MetricsConfig.java\n│   ├── OpenApiConfig.java\n│   ├── RequestCorrelationFilter.java\n│   ├── TelegramConfig.java\n│   ├── TelegramHealthIndicator.java\n│   ├── WebConfig.java\n│   └── WebSocketConfig.java\n├── constants/\n│   ├── FileTypeConstants.java\n│   ├── MediaConstants.java\n│   └── ServiceDefaults.java\n├── controller/\n│   ├── FileController.java\n│   ├── GlobalExceptionHandler.java\n│   ├── TelegramController.java\n│   └── WebController.java\n├── dto/\n│   ├── ApiErrorResponse.java\n│   └── (request and response records)\n├── model/\n│   └── LogicalFileMetadata.java\n├── service/\n│   ├── FileService.java\n│   ├── MetadataIndexService.java\n│   └── TelegramService.java\n├── service/impl/\n│   ├── FileServiceImpl.java\n│   ├── MetadataIndexServiceImpl.java\n│   ├── TdLibPreconditions.java\n│   ├── TelegramRateLimiter.java\n│   ├── TelegramServiceImpl.java\n│   └── TemporaryUploadJanitor.java\n└── websocket/\n    └── DownloadProgressWebSocketHandler.java\n\nsrc/main/resources/\n├── application.example.properties\n├── logback-spring.xml\n├── messages.properties\n├── messages_es.properties\n├── static/\n│   ├── css/\n│   └── js/\n└── templates/\n    ├── dashboard.html\n    ├── files.html\n    ├── folder-files.html\n    ├── folders.html\n    ├── stats.html\n    └── layout/\n\nDockerfile\ndocker-compose.yml\n```\n\n---\n\n## Testing\n\nRun the unit test suite:\n\n```bash\nmvn test\n```\n\nCompile without tests:\n\n```bash\nmvn -DskipTests compile\n```\n\n\u003e Integration tests that load the full Spring context require a TDLib native library and a valid Telegram session. Unit tests run independently.\n\n---\n\n## Operational notes\n\n### Download state\nA file is considered downloaded only when TDLib reports a completed local copy and that file exists on disk. A local file used only as an upload source is **not** considered a downloaded file.\n\n### Upload staging\nUploads are staged in `tdlib/upload-staging/`. These files exist only to provide TDLib with a stable local path during upload. After TDLib confirms the upload, the staged file is cleaned up. Stale leftovers are removed by the startup janitor.\n\n### Caching\nThe application uses Caffeine for in-memory metadata caching. This cache is a performance layer only; it is not a source of truth for file download state.\n\n### Logging\nIn the default profile, logs are written in a human-readable format with request correlation IDs. In the `prod` profile, logs are emitted as structured JSON for log aggregation systems.\n\nEvery HTTP request is tagged with an `X-Request-Id` header that appears in all related log entries.\n\n---\n\n## Troubleshooting\n\n### TDLib database lock errors\nIf you see errors about locking `td.binlog`, another process is already using the same TDLib directory. Stop the other process and restart Nekolu.\n\n### Unauthorized / authentication errors\nIf Telegram operations fail because the client is not authorized, authenticate TDLib first:\n\n```bash\njava --enable-native-access=ALL-UNNAMED -Djava.library.path=lib -cp lib/tdlib.jar org.drinkless.tdlib.example.Example\n```\n\nAfter successful authentication, quit the TDLib example client, ensure the same `tdlib/` directory is configured, and restart Nekolu.\n\n### `PHONE_NUMBER_INVALID`\nRe-enter the phone number in international format with the leading `+` and country code.\n\n### File can be listed but not viewed inline\nInline preview requires a fully downloaded local copy. If the file is still pending, trigger a download first.\n\n\n---\n\n## Notes for maintainers\n\n- Keep all public-facing API documentation in English\n- Prefer updating OpenAPI annotations when changing request/response contracts\n- Keep this README aligned with the actual screens, modules, and endpoints in the codebase\n- Never commit real Telegram credentials or session data to version control\n\n---\n\n## Support Development\n\nIf Nekolu is useful to you, consider buying me a coffee!\n\n\u003ca href=\"https://ko-fi.com/C0C01Y1SQI\" target=\"_blank\"\u003e\u003cimg height=\"26\" src=\"https://img.shields.io/badge/Donate-Ko--fi-FF5E5B?style=for-the-badge\u0026logo=ko-fi\u0026logoColor=white\" alt=\"Donate with Ko-fi\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://buy.stripe.com/00wcN67J46kl8LY8GYfMA01\" target=\"_blank\"\u003e\u003cimg height=\"26\" src=\"https://img.shields.io/badge/Donate-Stripe-635bff?style=for-the-badge\u0026logo=stripe\u0026logoColor=white\" alt=\"Donate with Stripe\" /\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzademy%2Fnekolu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzademy%2Fnekolu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzademy%2Fnekolu/lists"}