| { |
| "entities": { |
| "User": { |
| "$schema": "http://json-schema.org/draft-07/schema#", |
| "title": "User", |
| "type": "object", |
| "description": "Represents an individual user of the ByteBrief application, managing their uploaded documents and generated summaries. User authentication details are handled by an external system, with only non-sensitive profile information stored here.", |
| "properties": { |
| "id": { |
| "type": "string", |
| "description": "Unique identifier for the User entity." |
| }, |
| "email": { |
| "type": "string", |
| "description": "The primary email address of the user, used for identification within the application.", |
| "format": "email" |
| }, |
| "firstName": { |
| "type": "string", |
| "description": "The first name of the user." |
| }, |
| "lastName": { |
| "type": "string", |
| "description": "The last name of the user." |
| }, |
| "createdAt": { |
| "type": "string", |
| "description": "Timestamp indicating when the user account was created.", |
| "format": "date-time" |
| }, |
| "updatedAt": { |
| "type": "string", |
| "description": "Timestamp indicating when the user account was last updated.", |
| "format": "date-time" |
| } |
| }, |
| "required": [ |
| "id", |
| "email", |
| "firstName", |
| "lastName", |
| "createdAt", |
| "updatedAt" |
| ] |
| }, |
| "Document": { |
| "$schema": "http://json-schema.org/draft-07/schema#", |
| "title": "Document", |
| "type": "object", |
| "description": "Represents a document or file uploaded by a user for summarization, securely stored in a cloud-based storage solution.", |
| "properties": { |
| "id": { |
| "type": "string", |
| "description": "Unique identifier for the Document entity." |
| }, |
| "userId": { |
| "type": "string", |
| "description": "Reference to the User who uploaded this document. (Relationship: User 1:N Document)" |
| }, |
| "fileName": { |
| "type": "string", |
| "description": "The original name of the file when it was uploaded by the user." |
| }, |
| "cloudStoragePath": { |
| "type": "string", |
| "description": "The unique path or key identifying the document's location within the cloud storage system (e.g., S3 object key, Blob storage path)." |
| }, |
| "fileType": { |
| "type": "string", |
| "description": "The MIME type or file extension of the uploaded document (e.g., 'application/pdf', 'text/plain', 'image/jpeg')." |
| }, |
| "fileSize": { |
| "type": "number", |
| "description": "The size of the document in bytes." |
| }, |
| "uploadDate": { |
| "type": "string", |
| "description": "Timestamp indicating when the document was uploaded by the user.", |
| "format": "date-time" |
| }, |
| "status": { |
| "type": "string", |
| "description": "The current processing status of the document (e.g., 'uploaded', 'processing', 'summarized', 'failed', 'deleted')." |
| }, |
| "content": { |
| "type": "string", |
| "description": "The full text content of the document." |
| }, |
| "originalContentHash": { |
| "type": "string", |
| "description": "A cryptographic hash (e.g., SHA256) of the document's content, useful for integrity checks or duplicate detection." |
| } |
| }, |
| "required": [ |
| "id", |
| "userId", |
| "fileName", |
| "fileType", |
| "fileSize", |
| "uploadDate", |
| "status" |
| ] |
| }, |
| "Summary": { |
| "$schema": "http://json-schema.org/draft-07/schema#", |
| "title": "Summary", |
| "type": "object", |
| "description": "Represents an AI-generated summary of a specific document, linked to the original document and the owning user.", |
| "properties": { |
| "id": { |
| "type": "string", |
| "description": "Unique identifier for the Summary entity." |
| }, |
| "documentId": { |
| "type": "string", |
| "description": "Reference to the Document for which this summary was generated. (Relationship: Document 1:N Summary)" |
| }, |
| "userId": { |
| "type": "string", |
| "description": "Reference to the User who owns this summary (derived from the document's owner). (Relationship: User 1:N Summary)" |
| }, |
| "summaryText": { |
| "type": "string", |
| "description": "The actual text content of the AI-generated summary." |
| }, |
| "summaryLength": { |
| "type": "string", |
| "description": "Describes the intended length or verbosity of the summary (e.g., 'concise', 'detailed', 'keypoints')." |
| }, |
| "generatedAt": { |
| "type": "string", |
| "description": "Timestamp indicating when the summary was generated by the AI.", |
| "format": "date-time" |
| }, |
| "aiModelUsed": { |
| "type": "string", |
| "description": "Identifier for the specific AI model or configuration that was used to generate this summary." |
| }, |
| "version": { |
| "type": "number", |
| "description": "A version number for the summary, useful if multiple summaries are generated or re-generated for the same document over time." |
| } |
| }, |
| "required": [ |
| "id", |
| "documentId", |
| "userId", |
| "summaryText", |
| "generatedAt" |
| ] |
| } |
| }, |
| "auth": { |
| "providers": [ |
| "password" |
| ] |
| }, |
| "firestore": { |
| "structure": [ |
| { |
| "path": "/users/{userId}", |
| "definition": { |
| "entityName": "User", |
| "schema": { |
| "$ref": "#/backend/entities/User" |
| }, |
| "description": "Stores individual user profiles. This collection is owned by the user, and access is restricted to the authenticated user matching 'userId'.", |
| "params": [ |
| { |
| "name": "userId", |
| "description": "The unique identifier of the user, corresponding to Firebase Authentication UID." |
| } |
| ] |
| } |
| }, |
| { |
| "path": "/users/{userId}/documents/{documentId}", |
| "definition": { |
| "entityName": "Document", |
| "schema": { |
| "$ref": "#/backend/entities/Document" |
| }, |
| "description": "A subcollection under a specific user, containing metadata for documents uploaded by that user. Each document includes the denormalized 'userId' field for authorization independence.", |
| "params": [ |
| { |
| "name": "userId", |
| "description": "The unique identifier of the user who owns this document, corresponding to Firebase Authentication UID." |
| }, |
| { |
| "name": "documentId", |
| "description": "The unique identifier for the specific document." |
| } |
| ] |
| } |
| }, |
| { |
| "path": "/users/{userId}/documents/{documentId}/summaries/{summaryId}", |
| "definition": { |
| "entityName": "Summary", |
| "schema": { |
| "$ref": "#/backend/entities/Summary" |
| }, |
| "description": "A nested subcollection under a specific document, containing AI-generated summaries for that document. Each summary includes denormalized 'userId' and 'documentId' fields for authorization independence.", |
| "params": [ |
| { |
| "name": "userId", |
| "description": "The unique identifier of the user who owns this summary, corresponding to Firebase Authentication UID." |
| }, |
| { |
| "name": "documentId", |
| "description": "The unique identifier of the document this summary belongs to." |
| }, |
| { |
| "name": "summaryId", |
| "description": "The unique identifier for the specific summary." |
| } |
| ] |
| } |
| } |
| ], |
| "reasoning": "The Firestore structure for ByteBrief prioritizes secure, scalable, and debuggable access to user-owned documents and their summaries. It adheres strictly to the core design principles, especially Authorization Independence and the enforcement of Query-Authentic Paths (QAPs).\n\n1. **Users Collection (`/users/{userId}`):** This top-level collection stores individual user profiles. Access is strictly private, meaning only the authenticated user (`request.auth.uid`) can read or write their own user document. This path establishes path-based ownership, making user profile rules straightforward (e.g., `allow read, write: if request.auth.uid == userId;`).\n\n2. **Documents Subcollection (`/users/{userId}/documents/{documentId}`):** Each user's uploaded documents are stored as a subcollection under their respective user profile. This hierarchical structure inherently links a document to its owner. Critical for **Authorization Independence**, each `Document` document will explicitly contain a `userId` field (denormalized from the parent user). This allows security rules to verify ownership directly within the document (`resource.data.userId`) without requiring a `get()` call to the parent user document. This is crucial for enabling atomic operations and simplifying rule logic. This structure supports QAPs by allowing clients to query for documents belonging to `request.auth.uid` within the specific user's path, and rules can directly validate `request.auth.uid == userId` (the path wildcard).\n\n3. **Summaries Subcollection (`/users/{userId}/documents/{documentId}/summaries/{summaryId}`):** AI-generated summaries are stored as a further nested subcollection, under the specific document they relate to. This continues the strong hierarchical ownership model. For **Authorization Independence**, each `Summary` document will explicitly contain both `userId` and `documentId` fields. This denormalization allows security rules to verify both the user's ownership and the summary's linkage to the correct document without any `get()` operations on parent documents. This enables `allow read, write: if request.auth.uid == userId;` to be applied efficiently and securely. QAPs are supported by the nested path structure, allowing specific user and document-scoped queries where rules can directly check `request.auth.uid == userId` (the path wildcard).\n\n**Authorization Independence via Denormalization:** This design achieves Authorization Independence by explicitly embedding `userId` in every `Document` document and both `userId` and `documentId` in every `Summary` document. This means that any security rule for a `Document` or `Summary` can evaluate ownership (`request.auth.uid == resource.data.userId`) directly on the document being accessed, without needing to perform `get()` operations on parent collections to determine the owner. This is fundamental for robust and efficient security rules, particularly in transactions and batched writes.\n\n**Support for QAPs (Query-Authentic Paths):** The entire structure is designed around path-based ownership (`/users/{userId}/...`). This inherently makes `list` operations secure and efficient. When a user queries for their documents or summaries, they will naturally be querying within their specific `/users/{request.auth.uid}/` path. The security rules can then simply verify that `request.auth.uid` matches the `{userId}` wildcard in the path, ensuring that users can only query their own data. The explicit `userId` and `documentId` fields in the documents further reinforce this, allowing for rules like `allow read: if request.auth.uid == userId;` which are naturally query-authentic." |
| } |
| } |